From 14b9870a6ca2eafe049cbf36fa87e77dcec213bb Mon Sep 17 00:00:00 2001 From: Jan-Jelle Kester Date: Sat, 30 Oct 2021 12:46:26 +0200 Subject: [PATCH 1/9] Integration test provided in ticket --- src/it/mrm/repository/mph183-boot-bom-1.0.pom | 45 ++++++++++++++++++ src/it/mrm/repository/mph183-tool-bom-2.1.pom | 39 +++++++++++++++ .../mrm/repository/mph183-tool-lib1-2.1.pom | 28 +++++++++++ .../effective-pom-with-bom/invoker.properties | 18 +++++++ .../projects/effective-pom-with-bom/pom.xml | 47 +++++++++++++++++++ .../effective-pom-with-bom/test.properties | 19 ++++++++ 6 files changed, 196 insertions(+) create mode 100644 src/it/mrm/repository/mph183-boot-bom-1.0.pom create mode 100644 src/it/mrm/repository/mph183-tool-bom-2.1.pom create mode 100644 src/it/mrm/repository/mph183-tool-lib1-2.1.pom create mode 100644 src/it/projects/effective-pom-with-bom/invoker.properties create mode 100644 src/it/projects/effective-pom-with-bom/pom.xml create mode 100644 src/it/projects/effective-pom-with-bom/test.properties diff --git a/src/it/mrm/repository/mph183-boot-bom-1.0.pom b/src/it/mrm/repository/mph183-boot-bom-1.0.pom new file mode 100644 index 00000000..ef5d2f0d --- /dev/null +++ b/src/it/mrm/repository/mph183-boot-bom-1.0.pom @@ -0,0 +1,45 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.help.it + mph183-boot-bom + 1.0 + pom + + + 2.1 + + + + + + org.apache.maven.plugins.help.it + mph183-tool-bom + ${mph183-tool.version} + pom + import + + + + diff --git a/src/it/mrm/repository/mph183-tool-bom-2.1.pom b/src/it/mrm/repository/mph183-tool-bom-2.1.pom new file mode 100644 index 00000000..b0ae8b14 --- /dev/null +++ b/src/it/mrm/repository/mph183-tool-bom-2.1.pom @@ -0,0 +1,39 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.help.it + mph183-tool-bom + 2.1 + pom + + + + + org.apache.maven.plugins.help.it + mph183-tool-lib1 + 2.1 + + + + diff --git a/src/it/mrm/repository/mph183-tool-lib1-2.1.pom b/src/it/mrm/repository/mph183-tool-lib1-2.1.pom new file mode 100644 index 00000000..4d64ad95 --- /dev/null +++ b/src/it/mrm/repository/mph183-tool-lib1-2.1.pom @@ -0,0 +1,28 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.help.it + mph183-tool-lib1 + 2.1 + diff --git a/src/it/projects/effective-pom-with-bom/invoker.properties b/src/it/projects/effective-pom-with-bom/invoker.properties new file mode 100644 index 00000000..954ed85f --- /dev/null +++ b/src/it/projects/effective-pom-with-bom/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:effective-pom diff --git a/src/it/projects/effective-pom-with-bom/pom.xml b/src/it/projects/effective-pom-with-bom/pom.xml new file mode 100644 index 00000000..5b2cc839 --- /dev/null +++ b/src/it/projects/effective-pom-with-bom/pom.xml @@ -0,0 +1,47 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.help.it + mph183 + 1.0 + + + + + org.apache.maven.plugins.help.it + mph183-boot-bom + 1.0 + pom + import + + + + + + + org.apache.maven.plugins.help.it + mph183-tool-lib1 + + + diff --git a/src/it/projects/effective-pom-with-bom/test.properties b/src/it/projects/effective-pom-with-bom/test.properties new file mode 100644 index 00000000..707248e6 --- /dev/null +++ b/src/it/projects/effective-pom-with-bom/test.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output = result.txt +verbose = true From 4c1dd60bb4734913df51431aab9152f65d434382 Mon Sep 17 00:00:00 2001 From: Maarten Mulders Date: Tue, 5 Jul 2022 15:49:42 +0200 Subject: [PATCH 2/9] [MPH-183] Reflectively use enhanced formatting of InputLocations --- .../help/DefaultInputLocationFormatter.java | 46 ++++++++++ .../help/InputLocationFormatterFactory.java | 48 ++++++++++ .../help/Maven4InputLocationFormatter.java | 79 ++++++++++++++++ .../DefaultInputLocationFormatterTest.java | 92 +++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java create mode 100644 src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java create mode 100644 src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java create mode 100644 src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java diff --git a/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java new file mode 100644 index 00000000..41c67160 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java @@ -0,0 +1,46 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.codehaus.plexus.util.StringUtils; + +/** + * Maven 3.x-based implementation of {@link InputLocation.StringFormatter}. + */ +public class DefaultInputLocationFormatter extends InputLocation.StringFormatter +{ + @Override + public String toString( InputLocation location ) + { + InputSource source = location.getSource(); + + String s = source.getModelId(); // by default, display modelId + + if ( StringUtils.isBlank( s ) || s.contains( "[unknown-version]" ) ) + { + // unless it is blank or does not provide version information + s = source.toString(); + } + + return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + ' '; + } +} diff --git a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java new file mode 100644 index 00000000..76ed4d9a --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java @@ -0,0 +1,48 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.plugin.logging.Log; + +import java.lang.reflect.Method; + +/** + * Selects the most suitable implementation for {@link InputLocation.StringFormatter}. + */ +public class InputLocationFormatterFactory +{ + public static InputLocation.StringFormatter produce( final Log log ) + { + try + { + // This method was introduced in Maven 4. + Method getReferencedByMethod = InputLocation.class.getDeclaredMethod( "getReferencedBy", InputLocation.class ); + return new Maven4InputLocationFormatter( getReferencedByMethod ); + } + catch ( NoSuchMethodException nsme ) + { + // Fallback to pre-Maven 4 implementation. + log.info( "Unable to print chain of POM imports, falling back to printing the source POM " + + "without import information. This feature is available in Maven 4.0.0+." ); + return new DefaultInputLocationFormatter(); + } + } +} diff --git a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java new file mode 100644 index 00000000..001ae29a --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java @@ -0,0 +1,79 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.codehaus.plexus.util.StringUtils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Maven 4.x-based implementation of {@link InputLocation.StringFormatter}. Enhances the default implementation + * with support for following "references" (caused by e.g. dependency management imports). + */ +public class Maven4InputLocationFormatter extends InputLocation.StringFormatter +{ + private final Method getReferencedByMethod; + + public Maven4InputLocationFormatter( final Method getReferencedByMethod ) + { + this.getReferencedByMethod = getReferencedByMethod; + } + + @Override + public String toString( InputLocation location ) + { + InputSource source = location.getSource(); + + String s = source.getModelId(); // by default, display modelId + + if ( StringUtils.isBlank( s ) || s.contains( "[unknown-version]" ) ) + { + // unless it is blank or does not provide version information + s = source.toString(); + } + + InputLocation referencedBy = getReferencedBy( location ); + + StringBuilder p = new StringBuilder(); + + while ( referencedBy != null ) + { + p.append( " from " ).append( referencedBy.getSource().getModelId() ); + referencedBy = getReferencedBy( referencedBy ); + } + + return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + p; + } + + private InputLocation getReferencedBy( final InputLocation location ) + { + try + { + return (InputLocation) getReferencedByMethod.invoke( location ); + } + catch ( IllegalAccessException | InvocationTargetException e ) + { + throw new RuntimeException( e ); + } + } +} diff --git a/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java new file mode 100644 index 00000000..0db8ae04 --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java @@ -0,0 +1,92 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.core.StringContains.containsString; + +public class DefaultInputLocationFormatterTest +{ + private final InputLocation.StringFormatter formatter = new DefaultInputLocationFormatter(); + + @Test + public void withLineNumberShouldIncludeLineNumber() { + // Arrange + final InputSource source = new InputSource(); + source.setModelId( "foo:bar:1.0-SNAPSHOT" ); + source.setLocation( "/tmp/project/pom.xml" ); + final InputLocation location = new InputLocation( 3, 5, source ); + + // Act + final String result = formatter.toString( location ); + + // Assert + assertThat( result, containsString( "line 3" ) ); + } + + @Test + public void withoutLineNumberShouldNotIncludeLineNumber() { + // Arrange + final InputSource source = new InputSource(); + source.setModelId( "foo:bar:1.0-SNAPSHOT" ); + source.setLocation( "/tmp/project/pom.xml" ); + final InputLocation location = new InputLocation( -1, -1, source ); + + // Act + final String result = formatter.toString( location ); + + // Assert + assertThat( result, not( containsString( "line" ) ) ); + } + + @Test + public void withModelIdShouldIncludeModelId() { + // Arrange + final InputSource source = new InputSource(); + source.setModelId( "foo:bar:1.0-SNAPSHOT" ); + source.setLocation( "/tmp/project/pom.xml" ); + final InputLocation location = new InputLocation( 3, 5, source ); + + // Act + final String result = formatter.toString( location ); + + // Assert + assertThat( result, containsString( "foo:bar:1.0-SNAPSHOT" ) ); + } + + @Test + public void withoutModelIdShouldIncludeUnknownVersion() { + // Arrange + final InputSource source = new InputSource(); + source.setLocation( "/tmp/project/pom.xml" ); + final InputLocation location = new InputLocation( 3, 5, source ); + + // Act + final String result = formatter.toString( location ); + + // Assert + assertThat( result, not( containsString( "foo:bar:1.0-SNAPSHOT" ) ) ); + } +} \ No newline at end of file From 9ee51f7d4ecd1f009b22a8d0c9d1a4528b561b63 Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 1 Mar 2024 16:30:32 +0100 Subject: [PATCH 3/9] [MNG-7344] Experimental implementation to show the resolution path of boms --- .../help/InputLocationFormatterFactory.java | 7 ++- .../help/Maven4InputLocationFormatter.java | 56 +++++++++++++++---- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java index 76ed4d9a..9d514d5d 100644 --- a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java +++ b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java @@ -21,6 +21,7 @@ import org.apache.maven.model.InputLocation; import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; import java.lang.reflect.Method; @@ -29,13 +30,13 @@ */ public class InputLocationFormatterFactory { - public static InputLocation.StringFormatter produce( final Log log ) + public static InputLocation.StringFormatter produce( final Log log, final MavenProject project ) { try { // This method was introduced in Maven 4. - Method getReferencedByMethod = InputLocation.class.getDeclaredMethod( "getReferencedBy", InputLocation.class ); - return new Maven4InputLocationFormatter( getReferencedByMethod ); + Method getImportedFromMethod = InputLocation.class.getDeclaredMethod( "getImportedFrom" ); + return new Maven4InputLocationFormatter( getImportedFromMethod, project ); } catch ( NoSuchMethodException nsme ) { diff --git a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java index 001ae29a..72249adc 100644 --- a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java +++ b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java @@ -19,8 +19,10 @@ * under the License. */ +import org.apache.maven.model.Dependency; import org.apache.maven.model.InputLocation; import org.apache.maven.model.InputSource; +import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; import java.lang.reflect.InvocationTargetException; @@ -32,11 +34,13 @@ */ public class Maven4InputLocationFormatter extends InputLocation.StringFormatter { - private final Method getReferencedByMethod; + private final Method getImportedFromMethod; + private final MavenProject project; - public Maven4InputLocationFormatter( final Method getReferencedByMethod ) + public Maven4InputLocationFormatter( final Method getImportedFromMethod, final MavenProject project ) { - this.getReferencedByMethod = getReferencedByMethod; + this.getImportedFromMethod = getImportedFromMethod; + this.project = project; } @Override @@ -52,26 +56,58 @@ public String toString( InputLocation location ) s = source.toString(); } - InputLocation referencedBy = getReferencedBy( location ); + InputLocation importedFrom = getImportedFrom( location ); StringBuilder p = new StringBuilder(); - while ( referencedBy != null ) + while ( importedFrom != null ) { - p.append( " from " ).append( referencedBy.getSource().getModelId() ); - referencedBy = getReferencedBy( referencedBy ); + p.append( " from " ).append( importedFrom.getSource().getModelId() ); + importedFrom = getImportedFrom( importedFrom ); } return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + p; } - private InputLocation getReferencedBy( final InputLocation location ) + private InputLocation getImportedFrom( final InputLocation location ) { try { - return (InputLocation) getReferencedByMethod.invoke( location ); + InputLocation result = (InputLocation) getImportedFromMethod.invoke( location ); + + // TODO: Replace black magic + if ( result == null && project != null ) + { + for ( Dependency dependency : project.getDependencyManagement().getDependencies() ) + { + InputLocation groupIdLocation = dependency.getLocation( "groupId" ); + InputLocation artifactIdLocation = dependency.getLocation( "artifactId" ); + InputLocation versionLocation = dependency.getLocation( "version" ); + + if ( groupIdLocation != null && groupIdLocation.toString().equals( location.toString() ) ) + { + result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) + .invoke( dependency ); + break; + } + if ( artifactIdLocation != null && artifactIdLocation.toString().equals( location.toString() ) ) + { + result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) + .invoke( dependency ); + break; + } + if ( versionLocation != null && versionLocation.toString().equals( location.toString() ) ) + { + result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) + .invoke( dependency ); + break; + } + } + } + + return result; } - catch ( IllegalAccessException | InvocationTargetException e ) + catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) { throw new RuntimeException( e ); } From d87d4eadd4bfa810134d65a93f2dceb590416f3e Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 15 Mar 2024 11:17:57 +0100 Subject: [PATCH 4/9] Test for InputLocationFormatterFactory --- .../help/InputLocationFormatterFactory.java | 4 +- .../InputLocationFormatterFactoryTest.java | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java diff --git a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java index 9d514d5d..8712f8da 100644 --- a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java +++ b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java @@ -30,12 +30,14 @@ */ public class InputLocationFormatterFactory { + static Class inputLocationClass = InputLocation.class; + public static InputLocation.StringFormatter produce( final Log log, final MavenProject project ) { try { // This method was introduced in Maven 4. - Method getImportedFromMethod = InputLocation.class.getDeclaredMethod( "getImportedFrom" ); + Method getImportedFromMethod = inputLocationClass.getDeclaredMethod( "getImportedFrom" ); return new Maven4InputLocationFormatter( getImportedFromMethod, project ); } catch ( NoSuchMethodException nsme ) diff --git a/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java b/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java new file mode 100644 index 00000000..8f65db69 --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java @@ -0,0 +1,66 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.instanceOf; + +import static org.mockito.Mockito.*; + +public class InputLocationFormatterFactoryTest { + @Test + public void whenNoSuchMethodThrownReturnsDefaultInputLocationFormatter() { + // Arrange + final Log log = mock(Log.class); + final MavenProject project = mock(MavenProject.class); + + // Act + final InputLocation.StringFormatter result = InputLocationFormatterFactory.produce(log, project); + + // Assert + assertThat(result, instanceOf(DefaultInputLocationFormatter.class)); + } + + @Test + public void whenMethodExistsReturnsMaven4InputLocationFormatter() { + // Arrange + InputLocationFormatterFactory.inputLocationClass = Maven4InputLocationStub.class; + + final Log log = mock(Log.class); + final MavenProject project = mock(MavenProject.class); + + // Act + final InputLocation.StringFormatter result = InputLocationFormatterFactory.produce(log, project); + + // Assert + assertThat(result, instanceOf(Maven4InputLocationFormatter.class)); + } + + private static class Maven4InputLocationStub { + public String getImportedFrom() { + return ""; + } + } +} \ No newline at end of file From 2c4c9003454bd83142853a67bd94bd7f713c4fe5 Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 15 Mar 2024 14:47:00 +0100 Subject: [PATCH 5/9] Use all possible locations to determine imported from --- .../help/Maven4InputLocationFormatter.java | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java index 72249adc..e633db90 100644 --- a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java +++ b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java @@ -25,8 +25,11 @@ import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; /** * Maven 4.x-based implementation of {@link InputLocation.StringFormatter}. Enhances the default implementation @@ -69,47 +72,62 @@ public String toString( InputLocation location ) return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + p; } - private InputLocation getImportedFrom( final InputLocation location ) + protected InputLocation getImportedFrom( final InputLocation location ) { try { InputLocation result = (InputLocation) getImportedFromMethod.invoke( location ); - // TODO: Replace black magic if ( result == null && project != null ) { for ( Dependency dependency : project.getDependencyManagement().getDependencies() ) { - InputLocation groupIdLocation = dependency.getLocation( "groupId" ); - InputLocation artifactIdLocation = dependency.getLocation( "artifactId" ); - InputLocation versionLocation = dependency.getLocation( "version" ); - - if ( groupIdLocation != null && groupIdLocation.toString().equals( location.toString() ) ) - { - result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) - .invoke( dependency ); - break; - } - if ( artifactIdLocation != null && artifactIdLocation.toString().equals( location.toString() ) ) + Set locationKeys = getLocationKeys( dependency ); + for ( Object key : locationKeys ) { - result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) - .invoke( dependency ); - break; - } - if ( versionLocation != null && versionLocation.toString().equals( location.toString() ) ) - { - result = (InputLocation) Dependency.class.getMethod( "getImportedFrom" ) - .invoke( dependency ); - break; + if ( !( key instanceof String ) ) + { + throw new RuntimeException( "Expected a String, got " + key.getClass().getName() ); + } + + InputLocation dependencyLocation = dependency.getLocation( key ); + if ( dependencyLocation != null && dependencyLocation.toString().equals( location.toString() ) ) + { + result = ( InputLocation ) Dependency.class.getMethod( "getImportedFrom" ) + .invoke( dependency ); + break; + } } } } return result; } - catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) + catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException + | ClassNotFoundException e ) { throw new RuntimeException( e ); } } + + private Set getLocationKeys( Dependency dependency ) throws NoSuchFieldException, IllegalAccessException + , ClassNotFoundException + { + Field delegateField = Class.forName( "org.apache.maven.model.BaseObject" ).getDeclaredField( "delegate" ); + delegateField.setAccessible( true ); + Object delegate = delegateField.get( dependency ); + delegateField.setAccessible( false ); + + Field locationsField = delegate.getClass().getDeclaredField( "locations" ); + locationsField.setAccessible( true ); + Object locations = locationsField.get( delegate ); + locationsField.setAccessible( false ); + + if ( !( locations instanceof Map ) ) + { + throw new RuntimeException( "Expected a Map, got " + locations.getClass().getName() ); + } + + return ( (Map) locations ).keySet(); + } } From df09963a2c1636959f1c1aa85d0833eeefef26d1 Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 15 Mar 2024 14:49:16 +0100 Subject: [PATCH 6/9] Tests for Maven4InputLocationFormatter --- .../help/Maven4InputLocationFormatter.java | 1 + .../Maven4InputLocationFormatterTest.java | 125 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java diff --git a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java index e633db90..badf37ac 100644 --- a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java +++ b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java @@ -82,6 +82,7 @@ protected InputLocation getImportedFrom( final InputLocation location ) { for ( Dependency dependency : project.getDependencyManagement().getDependencies() ) { + // Until a new maven api model is released, we need to use reflection to access the locations Set locationKeys = getLocationKeys( dependency ); for ( Object key : locationKeys ) { diff --git a/src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java new file mode 100644 index 00000000..a840c582 --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java @@ -0,0 +1,125 @@ +package org.apache.maven.plugins.help; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.apache.maven.project.MavenProject; +import org.junit.Test; + +import java.util.Stack; + +import static org.junit.Assert.assertEquals; + +public class Maven4InputLocationFormatterTest { + + @Test + public void testImportedFromNullToString() { + // Arrange + final MavenProject project = new MavenProject(); + final Maven4InputLocationFormatter formatter = new Maven4InputLocationFormatterMock(project); + + final InputSource source = new InputSource(); + source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); + final InputLocation location = new InputLocation(7, 5, source); + + // Act + final String result = formatter.toString(location); + + // Assert + assertEquals("}org.example:MPG-183-project:1-SNAPSHOT, line 7", result); + } + + @Test + public void testImportedFromNotNullToString() { + // Arrange + final InputSource importedFromSource = new InputSource(); + importedFromSource.setModelId("org.example:MPG-183-bom:1-SNAPSHOT"); + final InputLocation importedFrom = new InputLocation(7, 5, importedFromSource); + + final MavenProject project = new MavenProject(); + final Maven4InputLocationFormatter formatter = new Maven4InputLocationFormatterMock(project, importedFrom); + + final InputSource source = new InputSource(); + source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); + final InputLocation location = new InputLocation(7, 5, source); + + // Act + final String result = formatter.toString(location); + + // Assert + assertEquals("}org.example:MPG-183-project:1-SNAPSHOT, line 7 from org.example:MPG-183-bom:1-SNAPSHOT", result); + } + + @Test + public void testMultipleImportedFromToString() { + // Arrange + final Maven4InputLocationFormatter formatter = createMultiImportedFromFormatter(); + + final InputSource source = new InputSource(); + source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); + final InputLocation location = new InputLocation(7, 5, source); + + // Act + final String result = formatter.toString(location); + + // Assert + assertEquals("}org.example:MPG-183-project:1-SNAPSHOT, line 7 from org.example:MPG-183-bom-2:1-SNAPSHOT from org.example:MPG-183-bom-1:1-SNAPSHOT", result); + } + + private static Maven4InputLocationFormatter createMultiImportedFromFormatter() { + final InputSource importedFromSource1 = new InputSource(); + importedFromSource1.setModelId("org.example:MPG-183-bom-1:1-SNAPSHOT"); + final InputLocation importedFrom1 = new InputLocation(7, 5, importedFromSource1); + + final InputSource importedFromSource2 = new InputSource(); + importedFromSource2.setModelId("org.example:MPG-183-bom-2:1-SNAPSHOT"); + final InputLocation importedFrom2 = new InputLocation(7, 5, importedFromSource2); + + return new Maven4InputLocationFormatterMock(new MavenProject(), importedFrom1, importedFrom2); + } + + private static class Maven4InputLocationFormatterMock extends Maven4InputLocationFormatter { + private final Stack mockedImportedFrom; + + public Maven4InputLocationFormatterMock(MavenProject project) { + this(project, new Stack<>()); + } + + public Maven4InputLocationFormatterMock(MavenProject project, Stack mockedImportedFrom) { + super(null, project); + this.mockedImportedFrom = mockedImportedFrom; + } + + public Maven4InputLocationFormatterMock(MavenProject project, InputLocation... mockedImportedFrom) { + super(null, project); + + this.mockedImportedFrom = new Stack<>(); + for (InputLocation location : mockedImportedFrom) { + this.mockedImportedFrom.push(location); + } + } + + @Override + protected InputLocation getImportedFrom(InputLocation location) { + return !mockedImportedFrom.isEmpty() ? mockedImportedFrom.pop() : null; + } + } +} \ No newline at end of file From 717687d9f71261a0fd412323f1e68c4bbb5ccbd0 Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 15 Mar 2024 14:54:14 +0100 Subject: [PATCH 7/9] Fix line ending --- .../effective-pom-with-bom/invoker.properties | 2 +- .../effective-pom-with-bom/test.properties | 38 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/it/projects/effective-pom-with-bom/invoker.properties b/src/it/projects/effective-pom-with-bom/invoker.properties index 954ed85f..bd7c354f 100644 --- a/src/it/projects/effective-pom-with-bom/invoker.properties +++ b/src/it/projects/effective-pom-with-bom/invoker.properties @@ -15,4 +15,4 @@ # specific language governing permissions and limitations # under the License. -invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:effective-pom +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:effective-pom \ No newline at end of file diff --git a/src/it/projects/effective-pom-with-bom/test.properties b/src/it/projects/effective-pom-with-bom/test.properties index 707248e6..a03a0265 100644 --- a/src/it/projects/effective-pom-with-bom/test.properties +++ b/src/it/projects/effective-pom-with-bom/test.properties @@ -1,19 +1,19 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -output = result.txt -verbose = true +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output = result.txt +verbose = true \ No newline at end of file From f22794d8abcf6a9c98003c4f4b8a30d2e4f10ba7 Mon Sep 17 00:00:00 2001 From: Juul Hobert Date: Fri, 15 Mar 2024 15:38:29 +0100 Subject: [PATCH 8/9] Renamed class, formatter can be made functional with maven 3.x --- .../help/DefaultInputLocationFormatter.java | 14 +- .../maven/plugins/help/EffectivePomMojo.java | 20 +-- .../help/ImportedFromLocationFormatter.java | 124 ++++++++++++++++ .../help/InputLocationFormatterFactory.java | 28 ++-- .../help/Maven4InputLocationFormatter.java | 134 ------------------ .../DefaultInputLocationFormatterTest.java | 48 +++---- ...=> ImportedFromLocationFormatterTest.java} | 33 ++--- .../InputLocationFormatterFactoryTest.java | 14 +- 8 files changed, 187 insertions(+), 228 deletions(-) create mode 100644 src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java delete mode 100644 src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java rename src/test/java/org/apache/maven/plugins/help/{Maven4InputLocationFormatterTest.java => ImportedFromLocationFormatterTest.java} (76%) diff --git a/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java index 41c67160..856c6dd1 100644 --- a/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java +++ b/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java @@ -1,5 +1,3 @@ -package org.apache.maven.plugins.help; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.plugins.help; import org.apache.maven.model.InputLocation; import org.apache.maven.model.InputSource; @@ -26,21 +25,18 @@ /** * Maven 3.x-based implementation of {@link InputLocation.StringFormatter}. */ -public class DefaultInputLocationFormatter extends InputLocation.StringFormatter -{ +public class DefaultInputLocationFormatter extends InputLocation.StringFormatter { @Override - public String toString( InputLocation location ) - { + public String toString(InputLocation location) { InputSource source = location.getSource(); String s = source.getModelId(); // by default, display modelId - if ( StringUtils.isBlank( s ) || s.contains( "[unknown-version]" ) ) - { + if (StringUtils.isBlank(s) || s.contains("[unknown-version]")) { // unless it is blank or does not provide version information s = source.toString(); } - return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + ' '; + return '}' + s + ((location.getLineNumber() >= 0) ? ", line " + location.getLineNumber() : "") + ' '; } } diff --git a/src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java b/src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java index c38e2c53..384cdabc 100644 --- a/src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java +++ b/src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java @@ -24,8 +24,6 @@ import java.util.List; import java.util.Properties; -import org.apache.maven.model.InputLocation; -import org.apache.maven.model.InputSource; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.model.io.xpp3.MavenXpp3WriterEx; @@ -176,7 +174,7 @@ private void writeEffectivePom(MavenProject project, XMLWriter writer) throws Mo try { if (verbose) { MavenXpp3WriterEx mavenXpp3WriterEx = new MavenXpp3WriterEx(); - mavenXpp3WriterEx.setStringFormatter(new InputLocationStringFormatter()); + mavenXpp3WriterEx.setStringFormatter(InputLocationFormatterFactory.produce(getLog(), project)); mavenXpp3WriterEx.write(sWriter, pom); } else { new MavenXpp3Writer().write(sWriter, pom); @@ -203,20 +201,4 @@ private static void cleanModel(Model pom) { properties.putAll(pom.getProperties()); pom.setProperties(properties); } - - private static class InputLocationStringFormatter extends InputLocation.StringFormatter { - @Override - public String toString(InputLocation location) { - InputSource source = location.getSource(); - - String s = source.getModelId(); // by default, display modelId - - if (StringUtils.isBlank(s) || s.contains("[unknown-version]")) { - // unless it is blank or does not provide version information - s = source.toString(); - } - - return '}' + s + ((location.getLineNumber() >= 0) ? ", line " + location.getLineNumber() : "") + ' '; - } - } } diff --git a/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java new file mode 100644 index 00000000..b3716d42 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.help; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.StringUtils; + +/** + * Implementation of {@link InputLocation.StringFormatter}. Enhances the default implementation with support for + * following "references" (caused by e.g. dependency management imports). + */ +public class ImportedFromLocationFormatter extends InputLocation.StringFormatter { + private final Method getImportedFromMethod; + private final MavenProject project; + + public ImportedFromLocationFormatter(final Method getImportedFromMethod, final MavenProject project) { + this.getImportedFromMethod = getImportedFromMethod; + this.project = project; + } + + @Override + public String toString(InputLocation location) { + InputSource source = location.getSource(); + + String s = source.getModelId(); // by default, display modelId + + if (StringUtils.isBlank(s) || s.contains("[unknown-version]")) { + // unless it is blank or does not provide version information + s = source.toString(); + } + + InputLocation importedFrom = getImportedFrom(location); + + StringBuilder p = new StringBuilder(); + + while (importedFrom != null) { + p.append(" from ").append(importedFrom.getSource().getModelId()); + importedFrom = getImportedFrom(importedFrom); + } + + return '}' + s + ((location.getLineNumber() >= 0) ? ", line " + location.getLineNumber() : "") + p; + } + + protected InputLocation getImportedFrom(final InputLocation location) { + try { + InputLocation result = (InputLocation) getImportedFromMethod.invoke(location); + + if (result == null && project != null) { + for (Dependency dependency : project.getDependencyManagement().getDependencies()) { + // Until a new maven api model is released, we need to use reflection to access the locations + Set locationKeys = getLocationKeys(dependency); + for (Object key : locationKeys) { + if (!(key instanceof String)) { + throw new RuntimeException( + "Expected a String, got " + key.getClass().getName()); + } + + InputLocation dependencyLocation = dependency.getLocation(key); + if (dependencyLocation != null + && dependencyLocation.toString().equals(location.toString())) { + result = (InputLocation) Dependency.class + .getMethod("getImportedFrom") + .invoke(dependency); + break; + } + } + } + } + + return result; + } catch (IllegalAccessException + | InvocationTargetException + | NoSuchMethodException + | NoSuchFieldException + | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private Set getLocationKeys(Dependency dependency) + throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { + Field delegateField = Class.forName("org.apache.maven.model.BaseObject").getDeclaredField("delegate"); + delegateField.setAccessible(true); + Object delegate = delegateField.get(dependency); + delegateField.setAccessible(false); + + Field locationsField = delegate.getClass().getDeclaredField("locations"); + locationsField.setAccessible(true); + Object locations = locationsField.get(delegate); + locationsField.setAccessible(false); + + if (!(locations instanceof Map)) { + throw new RuntimeException( + "Expected a Map, got " + locations.getClass().getName()); + } + + return ((Map) locations).keySet(); + } +} diff --git a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java index 8712f8da..048758c0 100644 --- a/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java +++ b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java @@ -1,5 +1,3 @@ -package org.apache.maven.plugins.help; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,33 +16,29 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.plugins.help; + +import java.lang.reflect.Method; import org.apache.maven.model.InputLocation; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; -import java.lang.reflect.Method; - /** * Selects the most suitable implementation for {@link InputLocation.StringFormatter}. */ -public class InputLocationFormatterFactory -{ +public class InputLocationFormatterFactory { static Class inputLocationClass = InputLocation.class; - public static InputLocation.StringFormatter produce( final Log log, final MavenProject project ) - { - try - { + public static InputLocation.StringFormatter produce(final Log log, final MavenProject project) { + try { // This method was introduced in Maven 4. - Method getImportedFromMethod = inputLocationClass.getDeclaredMethod( "getImportedFrom" ); - return new Maven4InputLocationFormatter( getImportedFromMethod, project ); - } - catch ( NoSuchMethodException nsme ) - { + Method getImportedFromMethod = inputLocationClass.getDeclaredMethod("getImportedFrom"); + return new ImportedFromLocationFormatter(getImportedFromMethod, project); + } catch (NoSuchMethodException nsme) { // Fallback to pre-Maven 4 implementation. - log.info( "Unable to print chain of POM imports, falling back to printing the source POM " - + "without import information. This feature is available in Maven 4.0.0+." ); + log.info("Unable to print chain of POM imports, falling back to printing the source POM " + + "without import information. This feature is available in Maven 4.0.0+."); return new DefaultInputLocationFormatter(); } } diff --git a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java deleted file mode 100644 index badf37ac..00000000 --- a/src/main/java/org/apache/maven/plugins/help/Maven4InputLocationFormatter.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.apache.maven.plugins.help; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import org.apache.maven.model.Dependency; -import org.apache.maven.model.InputLocation; -import org.apache.maven.model.InputSource; -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.StringUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Map; -import java.util.Set; - -/** - * Maven 4.x-based implementation of {@link InputLocation.StringFormatter}. Enhances the default implementation - * with support for following "references" (caused by e.g. dependency management imports). - */ -public class Maven4InputLocationFormatter extends InputLocation.StringFormatter -{ - private final Method getImportedFromMethod; - private final MavenProject project; - - public Maven4InputLocationFormatter( final Method getImportedFromMethod, final MavenProject project ) - { - this.getImportedFromMethod = getImportedFromMethod; - this.project = project; - } - - @Override - public String toString( InputLocation location ) - { - InputSource source = location.getSource(); - - String s = source.getModelId(); // by default, display modelId - - if ( StringUtils.isBlank( s ) || s.contains( "[unknown-version]" ) ) - { - // unless it is blank or does not provide version information - s = source.toString(); - } - - InputLocation importedFrom = getImportedFrom( location ); - - StringBuilder p = new StringBuilder(); - - while ( importedFrom != null ) - { - p.append( " from " ).append( importedFrom.getSource().getModelId() ); - importedFrom = getImportedFrom( importedFrom ); - } - - return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + p; - } - - protected InputLocation getImportedFrom( final InputLocation location ) - { - try - { - InputLocation result = (InputLocation) getImportedFromMethod.invoke( location ); - - if ( result == null && project != null ) - { - for ( Dependency dependency : project.getDependencyManagement().getDependencies() ) - { - // Until a new maven api model is released, we need to use reflection to access the locations - Set locationKeys = getLocationKeys( dependency ); - for ( Object key : locationKeys ) - { - if ( !( key instanceof String ) ) - { - throw new RuntimeException( "Expected a String, got " + key.getClass().getName() ); - } - - InputLocation dependencyLocation = dependency.getLocation( key ); - if ( dependencyLocation != null && dependencyLocation.toString().equals( location.toString() ) ) - { - result = ( InputLocation ) Dependency.class.getMethod( "getImportedFrom" ) - .invoke( dependency ); - break; - } - } - } - } - - return result; - } - catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException - | ClassNotFoundException e ) - { - throw new RuntimeException( e ); - } - } - - private Set getLocationKeys( Dependency dependency ) throws NoSuchFieldException, IllegalAccessException - , ClassNotFoundException - { - Field delegateField = Class.forName( "org.apache.maven.model.BaseObject" ).getDeclaredField( "delegate" ); - delegateField.setAccessible( true ); - Object delegate = delegateField.get( dependency ); - delegateField.setAccessible( false ); - - Field locationsField = delegate.getClass().getDeclaredField( "locations" ); - locationsField.setAccessible( true ); - Object locations = locationsField.get( delegate ); - locationsField.setAccessible( false ); - - if ( !( locations instanceof Map ) ) - { - throw new RuntimeException( "Expected a Map, got " + locations.getClass().getName() ); - } - - return ( (Map) locations ).keySet(); - } -} diff --git a/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java index 0db8ae04..288e4932 100644 --- a/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java +++ b/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java @@ -1,5 +1,3 @@ -package org.apache.maven.plugins.help; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,75 +16,75 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.plugins.help; import org.apache.maven.model.InputLocation; import org.apache.maven.model.InputSource; import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; -public class DefaultInputLocationFormatterTest -{ +public class DefaultInputLocationFormatterTest { private final InputLocation.StringFormatter formatter = new DefaultInputLocationFormatter(); @Test public void withLineNumberShouldIncludeLineNumber() { // Arrange final InputSource source = new InputSource(); - source.setModelId( "foo:bar:1.0-SNAPSHOT" ); - source.setLocation( "/tmp/project/pom.xml" ); - final InputLocation location = new InputLocation( 3, 5, source ); + source.setModelId("foo:bar:1.0-SNAPSHOT"); + source.setLocation("/tmp/project/pom.xml"); + final InputLocation location = new InputLocation(3, 5, source); // Act - final String result = formatter.toString( location ); + final String result = formatter.toString(location); // Assert - assertThat( result, containsString( "line 3" ) ); + assertThat(result, containsString("line 3")); } @Test public void withoutLineNumberShouldNotIncludeLineNumber() { // Arrange final InputSource source = new InputSource(); - source.setModelId( "foo:bar:1.0-SNAPSHOT" ); - source.setLocation( "/tmp/project/pom.xml" ); - final InputLocation location = new InputLocation( -1, -1, source ); + source.setModelId("foo:bar:1.0-SNAPSHOT"); + source.setLocation("/tmp/project/pom.xml"); + final InputLocation location = new InputLocation(-1, -1, source); // Act - final String result = formatter.toString( location ); + final String result = formatter.toString(location); // Assert - assertThat( result, not( containsString( "line" ) ) ); + assertThat(result, not(containsString("line"))); } @Test public void withModelIdShouldIncludeModelId() { // Arrange final InputSource source = new InputSource(); - source.setModelId( "foo:bar:1.0-SNAPSHOT" ); - source.setLocation( "/tmp/project/pom.xml" ); - final InputLocation location = new InputLocation( 3, 5, source ); + source.setModelId("foo:bar:1.0-SNAPSHOT"); + source.setLocation("/tmp/project/pom.xml"); + final InputLocation location = new InputLocation(3, 5, source); // Act - final String result = formatter.toString( location ); + final String result = formatter.toString(location); // Assert - assertThat( result, containsString( "foo:bar:1.0-SNAPSHOT" ) ); + assertThat(result, containsString("foo:bar:1.0-SNAPSHOT")); } @Test public void withoutModelIdShouldIncludeUnknownVersion() { // Arrange final InputSource source = new InputSource(); - source.setLocation( "/tmp/project/pom.xml" ); - final InputLocation location = new InputLocation( 3, 5, source ); + source.setLocation("/tmp/project/pom.xml"); + final InputLocation location = new InputLocation(3, 5, source); // Act - final String result = formatter.toString( location ); + final String result = formatter.toString(location); // Assert - assertThat( result, not( containsString( "foo:bar:1.0-SNAPSHOT" ) ) ); + assertThat(result, not(containsString("foo:bar:1.0-SNAPSHOT"))); } -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java similarity index 76% rename from src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java rename to src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java index a840c582..4184840d 100644 --- a/src/test/java/org/apache/maven/plugins/help/Maven4InputLocationFormatterTest.java +++ b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java @@ -1,5 +1,3 @@ -package org.apache.maven.plugins.help; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,23 +16,24 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.plugins.help; + +import java.util.Stack; import org.apache.maven.model.InputLocation; import org.apache.maven.model.InputSource; import org.apache.maven.project.MavenProject; import org.junit.Test; -import java.util.Stack; - import static org.junit.Assert.assertEquals; -public class Maven4InputLocationFormatterTest { +public class ImportedFromLocationFormatterTest { @Test public void testImportedFromNullToString() { // Arrange final MavenProject project = new MavenProject(); - final Maven4InputLocationFormatter formatter = new Maven4InputLocationFormatterMock(project); + final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(project); final InputSource source = new InputSource(); source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); @@ -55,7 +54,7 @@ public void testImportedFromNotNullToString() { final InputLocation importedFrom = new InputLocation(7, 5, importedFromSource); final MavenProject project = new MavenProject(); - final Maven4InputLocationFormatter formatter = new Maven4InputLocationFormatterMock(project, importedFrom); + final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(project, importedFrom); final InputSource source = new InputSource(); source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); @@ -71,7 +70,7 @@ public void testImportedFromNotNullToString() { @Test public void testMultipleImportedFromToString() { // Arrange - final Maven4InputLocationFormatter formatter = createMultiImportedFromFormatter(); + final ImportedFromLocationFormatter formatter = createMultiImportedFromFormatter(); final InputSource source = new InputSource(); source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); @@ -81,10 +80,12 @@ public void testMultipleImportedFromToString() { final String result = formatter.toString(location); // Assert - assertEquals("}org.example:MPG-183-project:1-SNAPSHOT, line 7 from org.example:MPG-183-bom-2:1-SNAPSHOT from org.example:MPG-183-bom-1:1-SNAPSHOT", result); + String expected = + "}org.example:MPG-183-project:1-SNAPSHOT, line 7 from org.example:MPG-183-bom-2:1-SNAPSHOT from org.example:MPG-183-bom-1:1-SNAPSHOT"; + assertEquals(expected, result); } - private static Maven4InputLocationFormatter createMultiImportedFromFormatter() { + private static ImportedFromLocationFormatter createMultiImportedFromFormatter() { final InputSource importedFromSource1 = new InputSource(); importedFromSource1.setModelId("org.example:MPG-183-bom-1:1-SNAPSHOT"); final InputLocation importedFrom1 = new InputLocation(7, 5, importedFromSource1); @@ -93,22 +94,22 @@ private static Maven4InputLocationFormatter createMultiImportedFromFormatter() { importedFromSource2.setModelId("org.example:MPG-183-bom-2:1-SNAPSHOT"); final InputLocation importedFrom2 = new InputLocation(7, 5, importedFromSource2); - return new Maven4InputLocationFormatterMock(new MavenProject(), importedFrom1, importedFrom2); + return new ImportedFromLocationFormatterMock(new MavenProject(), importedFrom1, importedFrom2); } - private static class Maven4InputLocationFormatterMock extends Maven4InputLocationFormatter { + private static class ImportedFromLocationFormatterMock extends ImportedFromLocationFormatter { private final Stack mockedImportedFrom; - public Maven4InputLocationFormatterMock(MavenProject project) { + public ImportedFromLocationFormatterMock(MavenProject project) { this(project, new Stack<>()); } - public Maven4InputLocationFormatterMock(MavenProject project, Stack mockedImportedFrom) { + public ImportedFromLocationFormatterMock(MavenProject project, Stack mockedImportedFrom) { super(null, project); this.mockedImportedFrom = mockedImportedFrom; } - public Maven4InputLocationFormatterMock(MavenProject project, InputLocation... mockedImportedFrom) { + public ImportedFromLocationFormatterMock(MavenProject project, InputLocation... mockedImportedFrom) { super(null, project); this.mockedImportedFrom = new Stack<>(); @@ -122,4 +123,4 @@ protected InputLocation getImportedFrom(InputLocation location) { return !mockedImportedFrom.isEmpty() ? mockedImportedFrom.pop() : null; } } -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java b/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java index 8f65db69..f383abb8 100644 --- a/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java +++ b/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java @@ -1,5 +1,3 @@ -package org.apache.maven.plugins.help; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,15 +16,15 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.maven.plugins.help; import org.apache.maven.model.InputLocation; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.instanceOf; - +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class InputLocationFormatterFactoryTest { @@ -46,7 +44,7 @@ public void whenNoSuchMethodThrownReturnsDefaultInputLocationFormatter() { @Test public void whenMethodExistsReturnsMaven4InputLocationFormatter() { // Arrange - InputLocationFormatterFactory.inputLocationClass = Maven4InputLocationStub.class; + InputLocationFormatterFactory.inputLocationClass = InputLocationStub.class; final Log log = mock(Log.class); final MavenProject project = mock(MavenProject.class); @@ -55,12 +53,12 @@ public void whenMethodExistsReturnsMaven4InputLocationFormatter() { final InputLocation.StringFormatter result = InputLocationFormatterFactory.produce(log, project); // Assert - assertThat(result, instanceOf(Maven4InputLocationFormatter.class)); + assertThat(result, instanceOf(ImportedFromLocationFormatter.class)); } - private static class Maven4InputLocationStub { + private static class InputLocationStub { public String getImportedFrom() { return ""; } } -} \ No newline at end of file +} From 2b2f6f1371eacddce53ba330be905ebc77e8c2e2 Mon Sep 17 00:00:00 2001 From: Giovanni van der Schelde Date: Fri, 29 Mar 2024 11:32:36 +0100 Subject: [PATCH 9/9] [MPH-183] Do not print same location twice and rename test methods --- .../help/ImportedFromLocationFormatter.java | 3 +- .../ImportedFromLocationFormatterTest.java | 31 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java b/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java index b3716d42..868bd5b5 100644 --- a/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java +++ b/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java @@ -58,7 +58,8 @@ public String toString(InputLocation location) { StringBuilder p = new StringBuilder(); - while (importedFrom != null) { + while (importedFrom != null + && !source.toString().equals(importedFrom.getSource().toString())) { p.append(" from ").append(importedFrom.getSource().getModelId()); importedFrom = getImportedFrom(importedFrom); } diff --git a/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java index 4184840d..0112ef17 100644 --- a/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java +++ b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java @@ -30,7 +30,7 @@ public class ImportedFromLocationFormatterTest { @Test - public void testImportedFromNullToString() { + public void testImportedFromSingleLocation() { // Arrange final MavenProject project = new MavenProject(); final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(project); @@ -47,7 +47,28 @@ public void testImportedFromNullToString() { } @Test - public void testImportedFromNotNullToString() { + public void testImportedFromDifferentLocation() { + // Arrange + final InputSource importedFromSource = new InputSource(); + importedFromSource.setModelId("org.example:MPG-183-bom2:1-SNAPSHOT"); + final InputLocation importedFrom = new InputLocation(7, 5, importedFromSource); + + final MavenProject project = new MavenProject(); + final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(project, importedFrom); + + final InputSource source = new InputSource(); + source.setModelId("org.example:MPG-183-bom1:1-SNAPSHOT"); + final InputLocation location = new InputLocation(7, 5, source); + + // Act + final String result = formatter.toString(location); + + // Assert + assertEquals("}org.example:MPG-183-bom1:1-SNAPSHOT, line 7 from org.example:MPG-183-bom2:1-SNAPSHOT", result); + } + + @Test + public void testImportedFromDoNotPrintSameLocationTwice() { // Arrange final InputSource importedFromSource = new InputSource(); importedFromSource.setModelId("org.example:MPG-183-bom:1-SNAPSHOT"); @@ -57,18 +78,18 @@ public void testImportedFromNotNullToString() { final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(project, importedFrom); final InputSource source = new InputSource(); - source.setModelId("org.example:MPG-183-project:1-SNAPSHOT"); + source.setModelId("org.example:MPG-183-bom:1-SNAPSHOT"); final InputLocation location = new InputLocation(7, 5, source); // Act final String result = formatter.toString(location); // Assert - assertEquals("}org.example:MPG-183-project:1-SNAPSHOT, line 7 from org.example:MPG-183-bom:1-SNAPSHOT", result); + assertEquals("}org.example:MPG-183-bom:1-SNAPSHOT, line 7", result); } @Test - public void testMultipleImportedFromToString() { + public void testImportedFromMultiLevelPrintsWithFrom() { // Arrange final ImportedFromLocationFormatter formatter = createMultiImportedFromFormatter();