diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java index 363b9f72..6e704c4f 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java @@ -24,6 +24,7 @@ import java.net.URL; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -73,24 +74,26 @@ public ProjectDependencyAnalysis analyze( MavenProject project ) Set declaredArtifacts = buildDeclaredArtifacts( project ); - Set usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses ); - Set mainUsedArtifacts = buildUsedArtifacts( artifactClassMap, mainDependencyClasses ); - - Set testArtifacts = buildUsedArtifacts( artifactClassMap, testOnlyDependencyClasses ); + Map> usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses ); + Set mainUsedArtifacts = buildUsedArtifacts( artifactClassMap, mainDependencyClasses ).keySet(); + + Set testArtifacts = buildUsedArtifacts( artifactClassMap, testOnlyDependencyClasses ).keySet(); Set testOnlyArtifacts = removeAll( testArtifacts, mainUsedArtifacts ); - + Set usedDeclaredArtifacts = new LinkedHashSet<>( declaredArtifacts ); - usedDeclaredArtifacts.retainAll( usedArtifacts ); + usedDeclaredArtifacts.retainAll( usedArtifacts.keySet() ); - Set usedUndeclaredArtifacts = new LinkedHashSet<>( usedArtifacts ); - usedUndeclaredArtifacts = removeAll( usedUndeclaredArtifacts, declaredArtifacts ); + Map> usedUndeclaredArtifactsWithClasses = new LinkedHashMap<>( usedArtifacts ); + Set usedUndeclaredArtifacts = removeAll( + usedUndeclaredArtifactsWithClasses.keySet(), declaredArtifacts ); + usedUndeclaredArtifactsWithClasses.keySet().retainAll( usedUndeclaredArtifacts ); Set unusedDeclaredArtifacts = new LinkedHashSet<>( declaredArtifacts ); - unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts ); + unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts.keySet() ); Set testArtifactsWithNonTestScope = getTestArtifactsWithNonTestScope( testOnlyArtifacts ); - return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts, + return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifactsWithClasses, unusedDeclaredArtifacts, testArtifactsWithNonTestScope ); } catch ( IOException exception ) @@ -259,10 +262,10 @@ private Set buildDeclaredArtifacts( MavenProject project ) return declaredArtifacts; } - private Set buildUsedArtifacts( Map> artifactClassMap, + private Map> buildUsedArtifacts( Map> artifactClassMap, Set dependencyClasses ) { - Set usedArtifacts = new HashSet<>(); + Map> usedArtifacts = new HashMap<>(); for ( String className : dependencyClasses ) { @@ -270,7 +273,13 @@ private Set buildUsedArtifacts( Map> artifactCla if ( artifact != null ) { - usedArtifacts.add( artifact ); + Set classesFromArtifact = usedArtifacts.get( artifact ); + if ( classesFromArtifact == null ) + { + classesFromArtifact = new HashSet(); + usedArtifacts.put( artifact, classesFromArtifact ); + } + classesFromArtifact.add( className ); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalysis.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalysis.java index d75ad4b0..0be4ffd3 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalysis.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalysis.java @@ -21,9 +21,11 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import org.apache.maven.artifact.Artifact; @@ -39,7 +41,7 @@ public class ProjectDependencyAnalysis private final Set usedDeclaredArtifacts; - private final Set usedUndeclaredArtifacts; + private final Map> usedUndeclaredArtifacts; private final Set unusedDeclaredArtifacts; @@ -50,7 +52,7 @@ public class ProjectDependencyAnalysis */ public ProjectDependencyAnalysis() { - this( null, null, null, null ); + this( null, (Map>) null, null, null ); } /** @@ -63,10 +65,8 @@ public ProjectDependencyAnalysis() public ProjectDependencyAnalysis( Set usedDeclaredArtifacts, Set usedUndeclaredArtifacts, Set unusedDeclaredArtifacts ) { - this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts ); - this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts ); - this.unusedDeclaredArtifacts = safeCopy( unusedDeclaredArtifacts ); - this.testArtifactsWithNonTestScope = new HashSet<>(); + this( usedDeclaredArtifacts, usedUndeclaredArtifacts, + unusedDeclaredArtifacts, Collections.emptySet() ); } /** @@ -80,6 +80,17 @@ public ProjectDependencyAnalysis( Set usedDeclaredArtifacts, Set usedDeclaredArtifacts, Set usedUndeclaredArtifacts, Set unusedDeclaredArtifacts, Set testArtifactsWithNonTestScope ) + { + this( usedDeclaredArtifacts, + mapWithKeys( usedUndeclaredArtifacts ), + unusedDeclaredArtifacts, + testArtifactsWithNonTestScope ); + } + + public ProjectDependencyAnalysis( Set usedDeclaredArtifacts, + Map> usedUndeclaredArtifacts, + Set unusedDeclaredArtifacts, + Set testArtifactsWithNonTestScope ) { this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts ); this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts ); @@ -103,6 +114,16 @@ public Set getUsedDeclaredArtifacts() * @return artifacts used but not declared */ public Set getUsedUndeclaredArtifacts() + { + return safeCopy( usedUndeclaredArtifacts.keySet() ); + } + + /** + * Returns artifacts used but not declared. + * + * @return artifacts used but not declared + */ + public Map> getUsedUndeclaredArtifactsWithClasses() { return safeCopy( usedUndeclaredArtifacts ); } @@ -297,4 +318,38 @@ private Set safeCopy( Set set ) { return ( set == null ) ? Collections.emptySet() : Collections.unmodifiableSet( new LinkedHashSet<>( set ) ); } + + private static Map> safeCopy( Map> origMap ) + { + if ( origMap == null ) + { + return Collections.emptyMap(); + } + + Map> map = new HashMap<>(); + + for ( Map.Entry> e : origMap.entrySet() ) + { + map.put( e.getKey(), Collections.unmodifiableSet( new LinkedHashSet<>( e.getValue() ) ) ); + } + + return map; + } + + private static Map> mapWithKeys( Set keys ) + { + if ( keys == null ) + { + return Collections.emptyMap(); + } + + Map> map = new HashMap<>(); + + for ( Artifact k : keys ) + { + map.put( k, Collections.emptySet() ); + } + + return map; + } } diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzerTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzerTest.java index 3c8516a3..097837d1 100644 --- a/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzerTest.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzerTest.java @@ -41,6 +41,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; @@ -182,7 +183,7 @@ public void testJarWithCompileDependency() "jarWithCompileDependency1", "jar", "1.0", "compile" ); Artifact guava = createArtifact( "com.google.guava", "guava", "jar", "30.1.1-android", "compile" ); Set usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, guava ) ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, null ); assertEquals( expectedAnalysis, actualAnalysis ); @@ -238,7 +239,7 @@ public void testJarWithTestDependency() Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "test" ); Set usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, junit ) ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, null ); + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, null ); assertEquals( expectedAnalysis, actualAnalysis ); } @@ -256,7 +257,7 @@ public void testJarWithXmlTransitiveDependency() Artifact jdom = createArtifact( "dom4j", "dom4j", "jar", "1.6.1", "compile" ); Set usedDeclaredArtifacts = Collections.singleton( jdom ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, null ); // MSHARED-47: usedUndeclaredArtifacts=[xml-apis:xml-apis:jar:1.0.b2:compile] @@ -279,7 +280,7 @@ public void testJarWithCompileScopedTestDependency() Set usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) ); Set nonTestScopedTestArtifacts = Collections.singleton( junit ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, nonTestScopedTestArtifacts ); assertEquals( expectedAnalysis, actualAnalysis ); @@ -300,7 +301,7 @@ public void testJarWithRuntimeScopedTestDependency() throws TestToolsException, Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "runtime" ); Set usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, null ); assertEquals( expectedAnalysis, actualAnalysis ); @@ -331,7 +332,7 @@ public void testMultimoduleProject() Artifact junit = createArtifact( "org.apache.maven.its.dependency", "test-module1", "jar", "1.0", "compile" ); Set usedDeclaredArtifacts = Collections.singleton( junit ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set) null, null, null ); assertEquals( expectedAnalysis, actualAnalysis ); @@ -353,7 +354,7 @@ public void testTypeUseAnnotationDependency() Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests", "typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" ); Set usedDeclaredArtifacts = Collections.singleton( annotation ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set) null, null, null ); assertEquals( expectedAnalysis, actualAnalysis ); @@ -375,7 +376,7 @@ public void testTypeUseAnnotationDependencyOnLocalVariable() Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests", "typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" ); Set usedDeclaredArtifacts = Collections.singleton( annotation ); - ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, null, null, + ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set) null, null, null); assertEquals( expectedAnalysis, actualAnalysis ); @@ -421,6 +422,27 @@ public void testJarWithClassInUnnamedPackage() assertEquals( expectedAnalysis, actualAnalysis ); } + @Test + public void testUsedUndeclaredClassReference() + throws TestToolsException, ProjectDependencyAnalyzerException + { + compileProject( "usedUndeclaredReference/pom.xml" ); + + MavenProject project = getProject( "usedUndeclaredReference/pom.xml" ); + + ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project ); + + Artifact xmlApis = createArtifact( "xml-apis", "xml-apis", "jar", "1.0.b2", "compile" ); + Set expectedUsedUndeclaredArtifacts = Collections.singleton( xmlApis ); + + assertEquals( expectedUsedUndeclaredArtifacts, actualAnalysis.getUsedUndeclaredArtifacts() ); + + Map> expectedUsedUndeclaredArtifactsWithClasses = + Collections.singletonMap(xmlApis, Collections.singleton("org.apache.xmlcommons.Version") ); + + assertEquals( expectedUsedUndeclaredArtifactsWithClasses, actualAnalysis.getUsedUndeclaredArtifactsWithClasses() ); + } + // private methods -------------------------------------------------------- private void compileProject( String pomPath ) diff --git a/src/test/resources/usedUndeclaredReference/pom.xml b/src/test/resources/usedUndeclaredReference/pom.xml new file mode 100644 index 00000000..2a8e6520 --- /dev/null +++ b/src/test/resources/usedUndeclaredReference/pom.xml @@ -0,0 +1,41 @@ + + + + + + 4.0.0 + + org.apache.maven.shared.dependency-analyzer.tests + usedUndeclaredReference + 1.0 + jar + + + + dom4j + dom4j + 1.6.1 + + + diff --git a/src/test/resources/usedUndeclaredReference/src/main/java/usedUndeclaredReference/Project.java b/src/test/resources/usedUndeclaredReference/src/main/java/usedUndeclaredReference/Project.java new file mode 100644 index 00000000..5432b405 --- /dev/null +++ b/src/test/resources/usedUndeclaredReference/src/main/java/usedUndeclaredReference/Project.java @@ -0,0 +1,25 @@ +package usedUndeclaredReference; + +/* + * 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. + */ + +public class Project +{ + public static final Class CLASS_REF = org.apache.xmlcommons.Version.class; +}