Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MDEP-779: include class names in used undeclared dependencies #44

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,24 +74,26 @@ public ProjectDependencyAnalysis analyze( MavenProject project )

Set<Artifact> declaredArtifacts = buildDeclaredArtifacts( project );

Set<Artifact> usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses );
Set<Artifact> mainUsedArtifacts = buildUsedArtifacts( artifactClassMap, mainDependencyClasses );
Set<Artifact> testArtifacts = buildUsedArtifacts( artifactClassMap, testOnlyDependencyClasses );
Map<Artifact, Set<String>> usedArtifacts = buildUsedArtifacts( artifactClassMap, dependencyClasses );
Set<Artifact> mainUsedArtifacts = buildUsedArtifacts( artifactClassMap, mainDependencyClasses ).keySet();

Set<Artifact> testArtifacts = buildUsedArtifacts( artifactClassMap, testOnlyDependencyClasses ).keySet();
Set<Artifact> testOnlyArtifacts = removeAll( testArtifacts, mainUsedArtifacts );

Set<Artifact> usedDeclaredArtifacts = new LinkedHashSet<>( declaredArtifacts );
usedDeclaredArtifacts.retainAll( usedArtifacts );
usedDeclaredArtifacts.retainAll( usedArtifacts.keySet() );

Set<Artifact> usedUndeclaredArtifacts = new LinkedHashSet<>( usedArtifacts );
usedUndeclaredArtifacts = removeAll( usedUndeclaredArtifacts, declaredArtifacts );
Map<Artifact, Set<String>> usedUndeclaredArtifactsWithClasses = new LinkedHashMap<>( usedArtifacts );
Set<Artifact> usedUndeclaredArtifacts = removeAll(
usedUndeclaredArtifactsWithClasses.keySet(), declaredArtifacts );
usedUndeclaredArtifactsWithClasses.keySet().retainAll( usedUndeclaredArtifacts );

Set<Artifact> unusedDeclaredArtifacts = new LinkedHashSet<>( declaredArtifacts );
unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts );
unusedDeclaredArtifacts = removeAll( unusedDeclaredArtifacts, usedArtifacts.keySet() );

Set<Artifact> testArtifactsWithNonTestScope = getTestArtifactsWithNonTestScope( testOnlyArtifacts );

return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts,
return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifactsWithClasses,
unusedDeclaredArtifacts, testArtifactsWithNonTestScope );
}
catch ( IOException exception )
Expand Down Expand Up @@ -259,18 +262,24 @@ private Set<Artifact> buildDeclaredArtifacts( MavenProject project )
return declaredArtifacts;
}

private Set<Artifact> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap,
private Map<Artifact, Set<String>> buildUsedArtifacts( Map<Artifact, Set<String>> artifactClassMap,
Set<String> dependencyClasses )
{
Set<Artifact> usedArtifacts = new HashSet<>();
Map<Artifact, Set<String>> usedArtifacts = new HashMap<>();

for ( String className : dependencyClasses )
{
Artifact artifact = findArtifactForClassName( artifactClassMap, className );

if ( artifact != null )
{
usedArtifacts.add( artifact );
Set<String> classesFromArtifact = usedArtifacts.get( artifact );
if ( classesFromArtifact == null )
{
classesFromArtifact = new HashSet<String>();
usedArtifacts.put( artifact, classesFromArtifact );
}
classesFromArtifact.add( className );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,7 +41,7 @@ public class ProjectDependencyAnalysis

private final Set<Artifact> usedDeclaredArtifacts;

private final Set<Artifact> usedUndeclaredArtifacts;
private final Map<Artifact, Set<String>> usedUndeclaredArtifacts;

private final Set<Artifact> unusedDeclaredArtifacts;

Expand All @@ -50,7 +52,7 @@ public class ProjectDependencyAnalysis
*/
public ProjectDependencyAnalysis()
{
this( null, null, null, null );
this( null, (Map<Artifact, Set<String>>) null, null, null );
}

/**
Expand All @@ -63,10 +65,8 @@ public ProjectDependencyAnalysis()
public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artifact> usedUndeclaredArtifacts,
Set<Artifact> unusedDeclaredArtifacts )
{
this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts );
this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts );
this.unusedDeclaredArtifacts = safeCopy( unusedDeclaredArtifacts );
this.testArtifactsWithNonTestScope = new HashSet<>();
this( usedDeclaredArtifacts, usedUndeclaredArtifacts,
unusedDeclaredArtifacts, Collections.<Artifact>emptySet() );
}

/**
Expand All @@ -80,6 +80,17 @@ public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artif
public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artifact> usedUndeclaredArtifacts,
Set<Artifact> unusedDeclaredArtifacts,
Set<Artifact> testArtifactsWithNonTestScope )
{
this( usedDeclaredArtifacts,
mapWithKeys( usedUndeclaredArtifacts ),
unusedDeclaredArtifacts,
testArtifactsWithNonTestScope );
}

public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts,
Map<Artifact, Set<String>> usedUndeclaredArtifacts,
Set<Artifact> unusedDeclaredArtifacts,
Set<Artifact> testArtifactsWithNonTestScope )
{
this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts );
this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts );
Expand All @@ -103,6 +114,16 @@ public Set<Artifact> getUsedDeclaredArtifacts()
* @return artifacts used but not declared
*/
public Set<Artifact> getUsedUndeclaredArtifacts()
{
return safeCopy( usedUndeclaredArtifacts.keySet() );
}

/**
* Returns artifacts used but not declared.
*
* @return artifacts used but not declared
*/
public Map<Artifact, Set<String>> getUsedUndeclaredArtifactsWithClasses()
{
return safeCopy( usedUndeclaredArtifacts );
}
Expand Down Expand Up @@ -297,4 +318,38 @@ private Set<Artifact> safeCopy( Set<Artifact> set )
{
return ( set == null ) ? Collections.emptySet() : Collections.unmodifiableSet( new LinkedHashSet<>( set ) );
}

private static Map<Artifact, Set<String>> safeCopy( Map<Artifact, Set<String>> origMap )
{
if ( origMap == null )
{
return Collections.emptyMap();
}

Map<Artifact, Set<String>> map = new HashMap<>();

for ( Map.Entry<Artifact, Set<String>> e : origMap.entrySet() )
{
map.put( e.getKey(), Collections.unmodifiableSet( new LinkedHashSet<>( e.getValue() ) ) );
}

return map;
}

private static Map<Artifact, Set<String>> mapWithKeys( Set<Artifact> keys )
{
if ( keys == null )
{
return Collections.emptyMap();
}

Map<Artifact, Set<String>> map = new HashMap<>();

for ( Artifact k : keys )
{
map.put( k, Collections.<String>emptySet() );
}

return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, guava ) );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
null );

assertEquals( expectedAnalysis, actualAnalysis );
Expand Down Expand Up @@ -238,7 +239,7 @@ public void testJarWithTestDependency()
Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "test" );

Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, junit ) );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null, null );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null, null );

assertEquals( expectedAnalysis, actualAnalysis );
}
Expand All @@ -256,7 +257,7 @@ public void testJarWithXmlTransitiveDependency()
Artifact jdom = createArtifact( "dom4j", "dom4j", "jar", "1.6.1", "compile" );
Set<Artifact> usedDeclaredArtifacts = Collections.singleton( jdom );

ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
null );

// MSHARED-47: usedUndeclaredArtifacts=[xml-apis:xml-apis:jar:1.0.b2:compile]
Expand All @@ -279,7 +280,7 @@ public void testJarWithCompileScopedTestDependency()

Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) );
Set<Artifact> nonTestScopedTestArtifacts = Collections.singleton( junit );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
nonTestScopedTestArtifacts );

assertEquals( expectedAnalysis, actualAnalysis );
Expand All @@ -300,7 +301,7 @@ public void testJarWithRuntimeScopedTestDependency() throws TestToolsException,
Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "runtime" );

Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
null );

assertEquals( expectedAnalysis, actualAnalysis );
Expand Down Expand Up @@ -331,7 +332,7 @@ public void testMultimoduleProject()
Artifact junit = createArtifact( "org.apache.maven.its.dependency", "test-module1", "jar", "1.0", "compile" );
Set<Artifact> usedDeclaredArtifacts = Collections.singleton( junit );

ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
null );

assertEquals( expectedAnalysis, actualAnalysis );
Expand All @@ -353,7 +354,7 @@ public void testTypeUseAnnotationDependency()
Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
"typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" );
Set<Artifact> usedDeclaredArtifacts = Collections.singleton( annotation );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set<Artifact>) null, null,
null );

assertEquals( expectedAnalysis, actualAnalysis );
Expand All @@ -375,7 +376,7 @@ public void testTypeUseAnnotationDependencyOnLocalVariable()
Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
"typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" );
Set<Artifact> usedDeclaredArtifacts = Collections.singleton( annotation );
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, null, null,
ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set<Artifact>) null, null,
null);

assertEquals( expectedAnalysis, actualAnalysis );
Expand Down Expand Up @@ -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<Artifact> expectedUsedUndeclaredArtifacts = Collections.singleton( xmlApis );

assertEquals( expectedUsedUndeclaredArtifacts, actualAnalysis.getUsedUndeclaredArtifacts() );

Map<Artifact, Set<String>> expectedUsedUndeclaredArtifactsWithClasses =
Collections.singletonMap(xmlApis, Collections.singleton("org.apache.xmlcommons.Version") );

assertEquals( expectedUsedUndeclaredArtifactsWithClasses, actualAnalysis.getUsedUndeclaredArtifactsWithClasses() );
}

// private methods --------------------------------------------------------

private void compileProject( String pomPath )
Expand Down
41 changes: 41 additions & 0 deletions src/test/resources/usedUndeclaredReference/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
~ 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.
-->

<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.shared.dependency-analyzer.tests</groupId>
<artifactId>usedUndeclaredReference</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -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;
}