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..bd7c354f
--- /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
\ No newline at end of file
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..a03a0265
--- /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
\ No newline at end of file
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..856c6dd1
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/help/DefaultInputLocationFormatter.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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/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..868bd5b5
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/help/ImportedFromLocationFormatter.java
@@ -0,0 +1,125 @@
+/*
+ * 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
+ && !source.toString().equals(importedFrom.getSource().toString())) {
+ 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
new file mode 100644
index 00000000..048758c0
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/help/InputLocationFormatterFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.Method;
+
+import org.apache.maven.model.InputLocation;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Selects the most suitable implementation for {@link InputLocation.StringFormatter}.
+ */
+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 = 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+.");
+ return new DefaultInputLocationFormatter();
+ }
+ }
+}
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..288e4932
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/help/DefaultInputLocationFormatterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 org.apache.maven.model.InputLocation;
+import org.apache.maven.model.InputSource;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+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")));
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java
new file mode 100644
index 00000000..0112ef17
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/help/ImportedFromLocationFormatterTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.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 static org.junit.Assert.assertEquals;
+
+public class ImportedFromLocationFormatterTest {
+
+ @Test
+ public void testImportedFromSingleLocation() {
+ // Arrange
+ final MavenProject project = new MavenProject();
+ final ImportedFromLocationFormatter formatter = new ImportedFromLocationFormatterMock(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 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");
+ 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-bom:1-SNAPSHOT");
+ final InputLocation location = new InputLocation(7, 5, source);
+
+ // Act
+ final String result = formatter.toString(location);
+
+ // Assert
+ assertEquals("}org.example:MPG-183-bom:1-SNAPSHOT, line 7", result);
+ }
+
+ @Test
+ public void testImportedFromMultiLevelPrintsWithFrom() {
+ // Arrange
+ final ImportedFromLocationFormatter 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
+ 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 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);
+
+ 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 ImportedFromLocationFormatterMock(new MavenProject(), importedFrom1, importedFrom2);
+ }
+
+ private static class ImportedFromLocationFormatterMock extends ImportedFromLocationFormatter {
+ private final Stack mockedImportedFrom;
+
+ public ImportedFromLocationFormatterMock(MavenProject project) {
+ this(project, new Stack<>());
+ }
+
+ public ImportedFromLocationFormatterMock(MavenProject project, Stack mockedImportedFrom) {
+ super(null, project);
+ this.mockedImportedFrom = mockedImportedFrom;
+ }
+
+ public ImportedFromLocationFormatterMock(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;
+ }
+ }
+}
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..f383abb8
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/help/InputLocationFormatterFactoryTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 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.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+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 = InputLocationStub.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(ImportedFromLocationFormatter.class));
+ }
+
+ private static class InputLocationStub {
+ public String getImportedFrom() {
+ return "";
+ }
+ }
+}