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 c7925716..c943a5b2 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 @@ -25,17 +25,11 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.*; import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Collectors; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.MavenProject; @@ -60,7 +54,9 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz @Inject private DependencyAnalyzer dependencyAnalyzer; - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public ProjectDependencyAnalysis analyze(MavenProject project, Collection excludedClasses) throws ProjectDependencyAnalyzerException { @@ -68,17 +64,17 @@ public ProjectDependencyAnalysis analyze(MavenProject project, Collection> artifactClassMap = buildArtifactClassMap(project, excludedClassesPatterns); - Set mainDependencyClasses = buildMainDependencyClasses(project, excludedClassesPatterns); - Set testDependencyClasses = buildTestDependencyClasses(project, excludedClassesPatterns); + Set mainDependencyClasses = buildMainDependencyClasses(project, excludedClassesPatterns); + Set testDependencyClasses = buildTestDependencyClasses(project, excludedClassesPatterns); - Set dependencyClasses = new HashSet<>(); + Set dependencyClasses = new HashSet<>(); dependencyClasses.addAll(mainDependencyClasses); dependencyClasses.addAll(testDependencyClasses); - Set testOnlyDependencyClasses = + Set testOnlyDependencyClasses = buildTestOnlyDependencyClasses(mainDependencyClasses, testDependencyClasses); - Map> usedArtifacts = buildUsedArtifacts(artifactClassMap, dependencyClasses); + Map> usedArtifacts = buildUsedArtifacts(artifactClassMap, dependencyClasses); Set mainUsedArtifacts = buildUsedArtifacts(artifactClassMap, mainDependencyClasses).keySet(); @@ -90,7 +86,7 @@ public ProjectDependencyAnalysis analyze(MavenProject project, Collection usedDeclaredArtifacts = new LinkedHashSet<>(declaredArtifacts); usedDeclaredArtifacts.retainAll(usedArtifacts.keySet()); - Map> usedUndeclaredArtifactsWithClasses = new LinkedHashMap<>(usedArtifacts); + Map> usedUndeclaredArtifactsWithClasses = new LinkedHashMap<>(usedArtifacts); Set usedUndeclaredArtifacts = removeAll(usedUndeclaredArtifactsWithClasses.keySet(), declaredArtifacts); usedUndeclaredArtifactsWithClasses.keySet().retainAll(usedUndeclaredArtifacts); @@ -190,29 +186,33 @@ private Map> buildArtifactClassMap(MavenProject project, C return artifactClassMap; } - private static Set buildTestOnlyDependencyClasses( - Set mainDependencyClasses, Set testDependencyClasses) { - Set testOnlyDependencyClasses = new HashSet<>(testDependencyClasses); - testOnlyDependencyClasses.removeAll(mainDependencyClasses); + private static Set buildTestOnlyDependencyClasses( + Set mainDependencyClasses, Set testDependencyClasses) { + Set testOnlyDependencyClasses = new HashSet<>(testDependencyClasses); + Set mainDepClassNames = mainDependencyClasses.stream() + .map(DependencyUsage::getDependencyClass) + .collect(Collectors.toSet()); + testOnlyDependencyClasses.removeIf(u -> mainDepClassNames.contains(u.getDependencyClass())); return testOnlyDependencyClasses; } - private Set buildMainDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) + private Set buildMainDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) throws IOException { String outputDirectory = project.getBuild().getOutputDirectory(); return buildDependencyClasses(outputDirectory, excludedClasses); } - private Set buildTestDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) + private Set buildTestDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) throws IOException { String testOutputDirectory = project.getBuild().getTestOutputDirectory(); return buildDependencyClasses(testOutputDirectory, excludedClasses); } - private Set buildDependencyClasses(String path, ClassesPatterns excludedClasses) throws IOException { + private Set buildDependencyClasses(String path, ClassesPatterns excludedClasses) + throws IOException { URL url = new File(path).toURI().toURL(); - return dependencyAnalyzer.analyze(url, excludedClasses); + return dependencyAnalyzer.analyzeUsages(url, excludedClasses); } private static Set buildDeclaredArtifacts(MavenProject project) { @@ -225,20 +225,20 @@ private static Set buildDeclaredArtifacts(MavenProject project) { return declaredArtifacts; } - private static Map> buildUsedArtifacts( - Map> artifactClassMap, Set dependencyClasses) { - Map> usedArtifacts = new HashMap<>(); + private static Map> buildUsedArtifacts( + Map> artifactClassMap, Set dependencyClasses) { + Map> usedArtifacts = new HashMap<>(); - for (String className : dependencyClasses) { - Artifact artifact = findArtifactForClassName(artifactClassMap, className); + for (DependencyUsage classUsage : dependencyClasses) { + Artifact artifact = findArtifactForClassName(artifactClassMap, classUsage.getDependencyClass()); if (artifact != null) { - Set classesFromArtifact = usedArtifacts.get(artifact); + Set classesFromArtifact = usedArtifacts.get(artifact); if (classesFromArtifact == null) { classesFromArtifact = new HashSet<>(); usedArtifacts.put(artifact, classesFromArtifact); } - classesFromArtifact.add(className); + classesFromArtifact.add(classUsage); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java index 22a6a276..e73bc56f 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.URL; import java.util.Set; +import java.util.stream.Collectors; /** * Gets the set of classes referenced by a library given either as a jar file or an exploded directory. @@ -48,5 +49,19 @@ default Set analyze(URL url) throws IOException { * @return the set of class names referenced by the library * @throws IOException if an error occurs reading a JAR or .class file */ - Set analyze(URL url, ClassesPatterns excludeClasses) throws IOException; + default Set analyze(URL url, ClassesPatterns excludeClasses) throws IOException { + return analyzeUsages(url, excludeClasses).stream() + .map(DependencyUsage::getDependencyClass) + .collect(Collectors.toSet()); + } + + /** + *

analyzeUsages.

+ * + * @param url the JAR file or directory to analyze + * @return the set of class names referenced by the library, paired with the + * classes declaring those references. + * @throws IOException if an error occurs reading a JAR or .class file + */ + Set analyzeUsages(URL url, ClassesPatterns excludeClasses) throws IOException; } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyUsage.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyUsage.java new file mode 100644 index 00000000..6b2109d2 --- /dev/null +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyUsage.java @@ -0,0 +1,92 @@ +/* + * 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.shared.dependency.analyzer; + +/** + * Usage of a dependency class by a project class. + * + * @author Jonathan Haber + */ +public class DependencyUsage { + + private final String dependencyClass; + + private final String usedBy; + + public DependencyUsage(String dependencyClass, String usedBy) { + this.dependencyClass = dependencyClass; + this.usedBy = usedBy; + } + + /** + * @return the dependency class used by the project class + */ + public String getDependencyClass() { + return dependencyClass; + } + + /** + * @return the project class using the dependency class + */ + public String getUsedBy() { + return usedBy; + } + + /* + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int hashCode = dependencyClass.hashCode(); + hashCode = (hashCode * 37) + usedBy.hashCode(); + + return hashCode; + } + + /* + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object object) { + if (object instanceof DependencyUsage) { + DependencyUsage usage = (DependencyUsage) object; + + return getDependencyClass().equals(usage.getDependencyClass()) + && getUsedBy().equals(usage.getUsedBy()); + } + + return false; + } + + /* + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append("dependencyClass=").append(getDependencyClass()); + buffer.append(","); + buffer.append("usedBy=").append(getUsedBy()); + + buffer.insert(0, "["); + buffer.insert(0, getClass().getName()); + + buffer.append("]"); + + return buffer.toString(); + } +} 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 1f71550a..e8feceea 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 @@ -18,14 +18,8 @@ */ package org.apache.maven.shared.dependency.analyzer; -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 java.util.*; +import java.util.stream.Collectors; import org.apache.maven.artifact.Artifact; @@ -39,7 +33,7 @@ public class ProjectDependencyAnalysis { private final Set usedDeclaredArtifacts; - private final Map> usedUndeclaredArtifacts; + private final Map> usedUndeclaredArtifacts; private final Set unusedDeclaredArtifacts; @@ -49,13 +43,13 @@ public class ProjectDependencyAnalysis { *

Constructor for ProjectDependencyAnalysis.

*/ public ProjectDependencyAnalysis() { - this(null, (Map>) null, null, null); + this(null, (Map>) null, null, null); } /** *

Constructor for ProjectDependencyAnalysis to maintain compatibility with old API

* - * @param usedDeclaredArtifacts artifacts both used and declared + * @param usedDeclaredArtifacts artifacts both used and declared * @param usedUndeclaredArtifacts artifacts used but not declared * @param unusedDeclaredArtifacts artifacts declared but not used */ @@ -69,9 +63,9 @@ public ProjectDependencyAnalysis( /** *

Constructor for ProjectDependencyAnalysis.

* - * @param usedDeclaredArtifacts artifacts both used and declared - * @param usedUndeclaredArtifacts artifacts used but not declared - * @param unusedDeclaredArtifacts artifacts declared but not used + * @param usedDeclaredArtifacts artifacts both used and declared + * @param usedUndeclaredArtifacts artifacts used but not declared + * @param unusedDeclaredArtifacts artifacts declared but not used * @param testArtifactsWithNonTestScope artifacts only used in tests but not declared with test scope */ public ProjectDependencyAnalysis( @@ -88,7 +82,7 @@ public ProjectDependencyAnalysis( public ProjectDependencyAnalysis( Set usedDeclaredArtifacts, - Map> usedUndeclaredArtifacts, + Map> usedUndeclaredArtifacts, Set unusedDeclaredArtifacts, Set testArtifactsWithNonTestScope) { this.usedDeclaredArtifacts = safeCopy(usedDeclaredArtifacts); @@ -121,6 +115,20 @@ public Set getUsedUndeclaredArtifacts() { * @return artifacts used but not declared */ public Map> getUsedUndeclaredArtifactsWithClasses() { + Map> usedUndeclaredArtifactsWithClasses = new HashMap<>(); + + for (Map.Entry> entry : usedUndeclaredArtifacts.entrySet()) { + usedUndeclaredArtifactsWithClasses.put( + entry.getKey(), + entry.getValue().stream() + .map(DependencyUsage::getDependencyClass) + .collect(Collectors.toSet())); + } + + return usedUndeclaredArtifactsWithClasses; + } + + public Map> getUsedUndeclaredArtifactsWithUsages() { return safeCopy(usedUndeclaredArtifacts); } @@ -136,7 +144,7 @@ public Set getUnusedDeclaredArtifacts() { /** * Returns artifacts only used in tests but not declared with test scope. * - * @return artifacts only used in tests but not declared with test scope + * @return artifacts only used in tests but not declared with test scope */ public Set getTestArtifactsWithNonTestScope() { return safeCopy(testArtifactsWithNonTestScope); @@ -228,7 +236,9 @@ public int hashCode() { return hashCode; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public boolean equals(Object object) { if (object instanceof ProjectDependencyAnalysis) { @@ -294,29 +304,29 @@ private Set safeCopy(Set set) { return (set == null) ? Collections.emptySet() : Collections.unmodifiableSet(new LinkedHashSet<>(set)); } - private static Map> safeCopy(Map> origMap) { + private static Map> safeCopy(Map> origMap) { if (origMap == null) { return Collections.emptyMap(); } - Map> map = new HashMap<>(); + Map> map = new HashMap<>(); - for (Map.Entry> e : origMap.entrySet()) { + for (Map.Entry> e : origMap.entrySet()) { map.put(e.getKey(), Collections.unmodifiableSet(new LinkedHashSet<>(e.getValue()))); } return map; } - private static Map> mapWithKeys(Set keys) { + private static Map> mapWithKeys(Set keys) { if (keys == null) { return Collections.emptyMap(); } - Map> map = new HashMap<>(); + Map> map = new HashMap<>(); for (Artifact k : keys) { - map.put(k, Collections.emptySet()); + map.put(k, Collections.emptySet()); } return map; diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java index 7b542cd6..4e85c7b8 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java @@ -28,6 +28,7 @@ import org.apache.maven.shared.dependency.analyzer.ClassFileVisitorUtils; import org.apache.maven.shared.dependency.analyzer.ClassesPatterns; import org.apache.maven.shared.dependency.analyzer.DependencyAnalyzer; +import org.apache.maven.shared.dependency.analyzer.DependencyUsage; /** * ASMDependencyAnalyzer @@ -39,11 +40,11 @@ public class ASMDependencyAnalyzer implements DependencyAnalyzer { @Override - public Set analyze(URL url, ClassesPatterns excludeClasses) throws IOException { + public Set analyzeUsages(URL url, ClassesPatterns excludeClasses) throws IOException { DependencyClassFileVisitor visitor = new DependencyClassFileVisitor(excludeClasses); ClassFileVisitorUtils.accept(url, visitor); - return visitor.getDependencies(); + return visitor.getDependencyUsages(); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultAnnotationVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultAnnotationVisitor.java index b5792731..1c7f4bf1 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultAnnotationVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultAnnotationVisitor.java @@ -31,39 +31,50 @@ public class DefaultAnnotationVisitor extends AnnotationVisitor { private final ResultCollector resultCollector; + private final String usedByClass; + /** *

Constructor for DefaultAnnotationVisitor.

* * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. */ - public DefaultAnnotationVisitor(ResultCollector resultCollector) { + public DefaultAnnotationVisitor(ResultCollector resultCollector, String usedByClass) { super(Opcodes.ASM9); this.resultCollector = resultCollector; + this.usedByClass = usedByClass; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visit(final String name, final Object value) { if (value instanceof Type) { - resultCollector.addType((Type) value); + resultCollector.addType(usedByClass, (Type) value); } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitEnum(final String name, final String desc, final String value) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitAnnotation(final String name, final String desc) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return this; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitArray(final String name) { return this; diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultClassVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultClassVisitor.java index 2d683e6f..2d37c180 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultClassVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultClassVisitor.java @@ -18,12 +18,7 @@ */ package org.apache.maven.shared.dependency.analyzer.asm; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; +import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureVisitor; @@ -44,37 +39,41 @@ public class DefaultClassVisitor extends ClassVisitor { private final MethodVisitor methodVisitor; + private final String usedByClass; + /** *

Constructor for DefaultClassVisitor.

* - * @param signatureVisitor a {@link org.objectweb.asm.signature.SignatureVisitor} object. + * @param signatureVisitor a {@link org.objectweb.asm.signature.SignatureVisitor} object. * @param annotationVisitor a {@link org.objectweb.asm.AnnotationVisitor} object. - * @param fieldVisitor a {@link org.objectweb.asm.FieldVisitor} object. - * @param methodVisitor a {@link org.objectweb.asm.MethodVisitor} object. - * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. + * @param fieldVisitor a {@link org.objectweb.asm.FieldVisitor} object. + * @param methodVisitor a {@link org.objectweb.asm.MethodVisitor} object. + * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. */ public DefaultClassVisitor( SignatureVisitor signatureVisitor, AnnotationVisitor annotationVisitor, FieldVisitor fieldVisitor, MethodVisitor methodVisitor, - ResultCollector resultCollector) { + ResultCollector resultCollector, + String usedByClass) { super(Opcodes.ASM9); this.signatureVisitor = signatureVisitor; this.annotationVisitor = annotationVisitor; this.fieldVisitor = fieldVisitor; this.methodVisitor = methodVisitor; this.resultCollector = resultCollector; + this.usedByClass = usedByClass; } /** *

visit.

* - * @param version a int. - * @param access a int. - * @param name a {@link java.lang.String} object. - * @param signature a {@link java.lang.String} object. - * @param superName a {@link java.lang.String} object. + * @param version a int. + * @param access a int. + * @param name a {@link java.lang.String} object. + * @param signature a {@link java.lang.String} object. + * @param superName a {@link java.lang.String} object. * @param interfaces an array of {@link java.lang.String} objects. */ @Override @@ -86,33 +85,37 @@ public void visit( final String superName, final String[] interfaces) { if (signature == null) { - resultCollector.addName(superName); - resultCollector.addNames(interfaces); + resultCollector.addName(usedByClass, superName); + resultCollector.addNames(usedByClass, interfaces); } else { addSignature(signature); } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public FieldVisitor visitField( final int access, final String name, final String desc, final String signature, final Object value) { if (signature == null) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); } else { addTypeSignature(signature); } if (value instanceof Type) { - resultCollector.addType((Type) value); + resultCollector.addType(usedByClass, (Type) value); } return fieldVisitor; @@ -121,10 +124,10 @@ public FieldVisitor visitField( /** *

visitMethod.

* - * @param access a int. - * @param name a {@link java.lang.String} object. - * @param desc a {@link java.lang.String} object. - * @param signature a {@link java.lang.String} object. + * @param access a int. + * @param name a {@link java.lang.String} object. + * @param desc a {@link java.lang.String} object. + * @param signature a {@link java.lang.String} object. * @param exceptions an array of {@link java.lang.String} objects. * @return a {@link org.objectweb.asm.MethodVisitor} object. */ @@ -132,26 +135,30 @@ public FieldVisitor visitField( public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { if (signature == null) { - resultCollector.addMethodDesc(desc); + resultCollector.addMethodDesc(usedByClass, desc); } else { addSignature(signature); } - resultCollector.addNames(exceptions); + resultCollector.addNames(usedByClass, exceptions); return methodVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitNestHost(final String nestHost) { - resultCollector.addName(nestHost); + resultCollector.addName(usedByClass, nestHost); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitNestMember(final String nestMember) { - resultCollector.addName(nestMember); + resultCollector.addName(usedByClass, nestMember); } private void addSignature(final String signature) { diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultFieldVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultFieldVisitor.java index 6f7e1407..074e35e7 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultFieldVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultFieldVisitor.java @@ -33,22 +33,28 @@ public class DefaultFieldVisitor extends FieldVisitor { private final ResultCollector resultCollector; + private final String usedByClass; + /** *

Constructor for DefaultFieldVisitor.

* * @param annotationVisitor a {@link org.objectweb.asm.AnnotationVisitor} object. - * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. + * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. */ - public DefaultFieldVisitor(AnnotationVisitor annotationVisitor, ResultCollector resultCollector) { + public DefaultFieldVisitor( + AnnotationVisitor annotationVisitor, ResultCollector resultCollector, String usedByClass) { super(Opcodes.ASM9); this.annotationVisitor = annotationVisitor; this.resultCollector = resultCollector; + this.usedByClass = usedByClass; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultMethodVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultMethodVisitor.java index d1b8d94a..c1b4ce7b 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultMethodVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultMethodVisitor.java @@ -43,68 +43,86 @@ public class DefaultMethodVisitor extends MethodVisitor { private final ResultCollector resultCollector; + private final String usedByClass; + /** *

Constructor for DefaultMethodVisitor.

* * @param annotationVisitor a {@link org.objectweb.asm.AnnotationVisitor} object. - * @param signatureVisitor a {@link org.objectweb.asm.signature.SignatureVisitor} object. - * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. + * @param signatureVisitor a {@link org.objectweb.asm.signature.SignatureVisitor} object. + * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. */ public DefaultMethodVisitor( - AnnotationVisitor annotationVisitor, SignatureVisitor signatureVisitor, ResultCollector resultCollector) { + AnnotationVisitor annotationVisitor, + SignatureVisitor signatureVisitor, + ResultCollector resultCollector, + String usedByClass) { super(Opcodes.ASM9); this.annotationVisitor = annotationVisitor; this.signatureVisitor = signatureVisitor; this.resultCollector = resultCollector; + this.usedByClass = usedByClass; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public AnnotationVisitor visitLocalVariableAnnotation( int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); return annotationVisitor; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitTypeInsn(final int opcode, final String desc) { if (desc.charAt(0) == '[') { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); } else { - resultCollector.addName(desc); + resultCollector.addName(usedByClass, desc); } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { - resultCollector.addName(owner); + resultCollector.addName(usedByClass, owner); /* * NOTE: Merely accessing a field does not impose a direct dependency on its type. For example, the code line * java.lang.Object var = bean.field; does not directly depend on the type of the field. A direct @@ -113,33 +131,43 @@ public void visitFieldInsn(final int opcode, final String owner, final String na */ } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - resultCollector.addName(owner); + resultCollector.addName(usedByClass, owner); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitLdcInsn(final Object cst) { if (cst instanceof Type) { - resultCollector.addType((Type) cst); + resultCollector.addType(usedByClass, (Type) cst); } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitMultiANewArrayInsn(final String desc, final int dims) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { - resultCollector.addName(type); + resultCollector.addName(usedByClass, type); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitLocalVariable( final String name, @@ -149,7 +177,7 @@ public void visitLocalVariable( final Label end, final int index) { if (signature == null) { - resultCollector.addDesc(desc); + resultCollector.addDesc(usedByClass, desc); } else { addTypeSignature(signature); } @@ -167,6 +195,6 @@ public void visitInvokeDynamicInsn( Arrays.stream(bootstrapMethodArguments) .filter(Type.class::isInstance) .map(Type.class::cast) - .forEach(resultCollector::addType); + .forEach(t -> resultCollector.addType(usedByClass, t)); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultSignatureVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultSignatureVisitor.java index d09bca37..89bd1a4a 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultSignatureVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DefaultSignatureVisitor.java @@ -29,26 +29,32 @@ */ public class DefaultSignatureVisitor extends SignatureVisitor { private final ResultCollector resultCollector; + private final String usedByClass; /** *

Constructor for DefaultSignatureVisitor.

* * @param resultCollector a {@link org.apache.maven.shared.dependency.analyzer.asm.ResultCollector} object. */ - public DefaultSignatureVisitor(ResultCollector resultCollector) { + public DefaultSignatureVisitor(ResultCollector resultCollector, String usedByClass) { super(Opcodes.ASM9); this.resultCollector = resultCollector; + this.usedByClass = usedByClass; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitClassType(final String name) { - resultCollector.addName(name); + resultCollector.addName(usedByClass, name); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitInnerClassType(final String name) { - resultCollector.addName(name); + resultCollector.addName(usedByClass, name); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java index 34dac5c1..7516381e 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java @@ -26,11 +26,8 @@ import org.apache.maven.shared.dependency.analyzer.ClassFileVisitor; import org.apache.maven.shared.dependency.analyzer.ClassesPatterns; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; +import org.apache.maven.shared.dependency.analyzer.DependencyUsage; +import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureVisitor; /** @@ -62,7 +59,9 @@ public DependencyClassFileVisitor() { this(new ClassesPatterns()); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void visitClass(String className, InputStream in) { try { @@ -76,15 +75,16 @@ public void visitClass(String className, InputStream in) { final Set constantPoolClassRefs = ConstantPoolParser.getConstantPoolClassReferences(byteCode); for (String string : constantPoolClassRefs) { - resultCollector.addName(string); + resultCollector.addName(className, string); } - AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(resultCollector); - SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(resultCollector); - FieldVisitor fieldVisitor = new DefaultFieldVisitor(annotationVisitor, resultCollector); - MethodVisitor mv = new DefaultMethodVisitor(annotationVisitor, signatureVisitor, resultCollector); - ClassVisitor classVisitor = - new DefaultClassVisitor(signatureVisitor, annotationVisitor, fieldVisitor, mv, resultCollector); + AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(resultCollector, className); + SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(resultCollector, className); + FieldVisitor fieldVisitor = new DefaultFieldVisitor(annotationVisitor, resultCollector, className); + MethodVisitor mv = + new DefaultMethodVisitor(annotationVisitor, signatureVisitor, resultCollector, className); + ClassVisitor classVisitor = new DefaultClassVisitor( + signatureVisitor, annotationVisitor, fieldVisitor, mv, resultCollector, className); reader.accept(classVisitor, 0); } catch (IOException exception) { @@ -116,4 +116,14 @@ private byte[] toByteArray(InputStream in) throws IOException { public Set getDependencies() { return resultCollector.getDependencies(); } + + /** + *

getDependencyUsages.

+ * + * @return the set of classes referenced by visited class files, paired with + * classes declaring the references. + */ + public Set getDependencyUsages() { + return resultCollector.getDependencyUsages(); + } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollector.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollector.java index ca9e8eea..65e39373 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollector.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollector.java @@ -20,7 +20,9 @@ import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; +import org.apache.maven.shared.dependency.analyzer.DependencyUsage; import org.objectweb.asm.Type; /** @@ -30,7 +32,7 @@ */ public class ResultCollector { - private final Set classes = new HashSet<>(); + private final Set classUsages = new HashSet<>(); /** *

getDependencies.

@@ -38,7 +40,18 @@ public class ResultCollector { * @return a {@link java.util.Set} object. */ public Set getDependencies() { - return classes; + return getDependencyUsages().stream() + .map(DependencyUsage::getDependencyClass) + .collect(Collectors.toSet()); + } + + /** + *

getDependencyUsages.

+ * + * @return a {@link java.util.Set} object. + */ + public Set getDependencyUsages() { + return classUsages; } /** @@ -46,7 +59,7 @@ public Set getDependencies() { * * @param name a {@link java.lang.String} object. */ - public void addName(String name) { + public void addName(final String usedByClass, String name) { if (name == null) { return; } @@ -65,25 +78,25 @@ public void addName(String name) { } // decode internal representation - add(name.replace('/', '.')); + add(usedByClass, name.replace('/', '.')); } - void addDesc(final String desc) { - addType(Type.getType(desc)); + void addDesc(final String usedByClass, final String desc) { + addType(usedByClass, Type.getType(desc)); } - void addType(final Type t) { + void addType(final String usedByClass, final Type t) { switch (t.getSort()) { case Type.ARRAY: - addType(t.getElementType()); + addType(usedByClass, t.getElementType()); break; case Type.METHOD: - addMethodDesc(t.getDescriptor()); + addMethodDesc(usedByClass, t.getDescriptor()); break; case Type.OBJECT: - addName(t.getClassName()); + addName(usedByClass, t.getClassName()); break; default: } @@ -94,30 +107,30 @@ void addType(final Type t) { * * @param name a {@link java.lang.String} object. */ - public void add(String name) { + public void add(final String usedByClass, final String name) { // inner classes have equivalent compilation requirement as container class if (name.indexOf('$') < 0) { - classes.add(name); + classUsages.add(new DependencyUsage(name, usedByClass)); } } - void addNames(final String[] names) { + void addNames(final String usedByClass, final String[] names) { if (names == null) { return; } for (String name : names) { - addName(name); + addName(usedByClass, name); } } - void addMethodDesc(final String desc) { - addType(Type.getReturnType(desc)); + void addMethodDesc(final String usedByClass, final String desc) { + addType(usedByClass, Type.getReturnType(desc)); Type[] types = Type.getArgumentTypes(desc); for (Type type : types) { - addType(type); + addType(usedByClass, type); } } } diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyVisitorTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyVisitorTest.java index cad3a2a7..0ed87b77 100644 --- a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyVisitorTest.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyVisitorTest.java @@ -20,13 +20,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.Attribute; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; +import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureVisitor; import static org.assertj.core.api.Assertions.assertThat; @@ -41,13 +35,16 @@ class DependencyVisitorTest { private DefaultClassVisitor visitor; private MethodVisitor mv; + private String usedByClass = "com.example.MyClass"; + @BeforeEach void setUp() { - AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(resultCollector); - SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(resultCollector); - FieldVisitor fieldVisitor = new DefaultFieldVisitor(annotationVisitor, resultCollector); - mv = new DefaultMethodVisitor(annotationVisitor, signatureVisitor, resultCollector); - visitor = new DefaultClassVisitor(signatureVisitor, annotationVisitor, fieldVisitor, mv, resultCollector); + AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(resultCollector, usedByClass); + SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(resultCollector, usedByClass); + FieldVisitor fieldVisitor = new DefaultFieldVisitor(annotationVisitor, resultCollector, usedByClass); + mv = new DefaultMethodVisitor(annotationVisitor, signatureVisitor, resultCollector, usedByClass); + visitor = new DefaultClassVisitor( + signatureVisitor, annotationVisitor, fieldVisitor, mv, resultCollector, usedByClass); } @Test