diff --git a/src/it/kotlinInnerClassReference/consumer/pom.xml b/src/it/kotlinInnerClassReference/consumer/pom.xml
new file mode 100644
index 0000000..7786632
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/consumer/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+ 4.0.0
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ kotlin-inner-class-reference
+ 1.0
+
+
+ kotlin-consumer
+
+
+ 2.4.10
+
+
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ nested-class-library
+ ${project.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${kotlin.version}
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ compile
+
+ compile
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.3.0
+
+
+ kotlin-sources
+ initialize
+
+ add-source
+
+
+
+ src/main/kotlin
+
+
+
+
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ maven-mock-plugin
+ 1.0
+
+
+
+ mock-analyze
+
+
+
+
+
+
+
diff --git a/src/it/kotlinInnerClassReference/consumer/src/main/kotlin/consumer/Example.kt b/src/it/kotlinInnerClassReference/consumer/src/main/kotlin/consumer/Example.kt
new file mode 100644
index 0000000..62f35b3
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/consumer/src/main/kotlin/consumer/Example.kt
@@ -0,0 +1,27 @@
+/*
+ * 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 consumer
+
+import library.Outer
+
+class Example {
+ fun createInner() {
+ Outer.Inner()
+ }
+}
diff --git a/src/it/kotlinInnerClassReference/library/pom.xml b/src/it/kotlinInnerClassReference/library/pom.xml
new file mode 100644
index 0000000..0f36940
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/library/pom.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ 4.0.0
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ kotlin-inner-class-reference
+ 1.0
+
+
+ nested-class-library
+
diff --git a/src/it/kotlinInnerClassReference/library/src/main/java/library/Outer.java b/src/it/kotlinInnerClassReference/library/src/main/java/library/Outer.java
new file mode 100644
index 0000000..2e0aca1
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/library/src/main/java/library/Outer.java
@@ -0,0 +1,23 @@
+/*
+ * 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 library;
+
+public class Outer {
+ public static class Inner {}
+}
diff --git a/src/it/kotlinInnerClassReference/pom.xml b/src/it/kotlinInnerClassReference/pom.xml
new file mode 100644
index 0000000..552992a
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ kotlin-inner-class-reference
+ 1.0
+ pom
+
+
+ library
+ consumer
+
+
diff --git a/src/it/kotlinInnerClassReference/verify.groovy b/src/it/kotlinInnerClassReference/verify.groovy
new file mode 100644
index 0000000..b7c782c
--- /dev/null
+++ b/src/it/kotlinInnerClassReference/verify.groovy
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+def analysis = new File( basedir, 'consumer/target/analysis.txt' ).text
+
+def expected = '''
+UsedDeclaredArtifacts:
+ org.apache.maven.shared.dependency-analyzer.tests:nested-class-library:jar:1.0:compile
+ org.jetbrains.kotlin:kotlin-stdlib:jar:2.4.10:compile
+
+UsedUndeclaredArtifactsWithClasses:
+
+UnusedDeclaredArtifacts:
+
+TestArtifactsWithNonTestScope:
+'''
+
+assert analysis == expected
diff --git a/src/it/topLevelDollarClassReference/consumer/pom.xml b/src/it/topLevelDollarClassReference/consumer/pom.xml
new file mode 100644
index 0000000..1476bf6
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/consumer/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+ 4.0.0
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ top-level-dollar-class-reference
+ 1.0
+
+
+ dollar-class-consumer
+
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ dollar-class-library
+ ${project.version}
+
+
+
+
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ maven-mock-plugin
+ 1.0
+
+
+
+ mock-analyze
+
+
+
+
+
+
+
diff --git a/src/it/topLevelDollarClassReference/consumer/src/main/java/consumer/Example.java b/src/it/topLevelDollarClassReference/consumer/src/main/java/consumer/Example.java
new file mode 100644
index 0000000..820ae36
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/consumer/src/main/java/consumer/Example.java
@@ -0,0 +1,27 @@
+/*
+ * 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 consumer;
+
+import library.Dollar$Class;
+
+public class Example {
+ public Dollar$Class create() {
+ return new Dollar$Class();
+ }
+}
diff --git a/src/it/topLevelDollarClassReference/library/pom.xml b/src/it/topLevelDollarClassReference/library/pom.xml
new file mode 100644
index 0000000..2050f8a
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/library/pom.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ 4.0.0
+
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ top-level-dollar-class-reference
+ 1.0
+
+
+ dollar-class-library
+
diff --git a/src/it/topLevelDollarClassReference/library/src/main/java/library/Dollar$Class.java b/src/it/topLevelDollarClassReference/library/src/main/java/library/Dollar$Class.java
new file mode 100644
index 0000000..66d2d7c
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/library/src/main/java/library/Dollar$Class.java
@@ -0,0 +1,21 @@
+/*
+ * 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 library;
+
+public class Dollar$Class {}
diff --git a/src/it/topLevelDollarClassReference/pom.xml b/src/it/topLevelDollarClassReference/pom.xml
new file mode 100644
index 0000000..8f537e9
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.shared.dependency-analyzer.tests
+ top-level-dollar-class-reference
+ 1.0
+ pom
+
+
+ library
+ consumer
+
+
diff --git a/src/it/topLevelDollarClassReference/verify.groovy b/src/it/topLevelDollarClassReference/verify.groovy
new file mode 100644
index 0000000..e2bd5a4
--- /dev/null
+++ b/src/it/topLevelDollarClassReference/verify.groovy
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+def analysis = new File( basedir, 'consumer/target/analysis.txt' ).text
+
+def expected = '''
+UsedDeclaredArtifacts:
+ org.apache.maven.shared.dependency-analyzer.tests:dollar-class-library:jar:1.0:compile
+
+UsedUndeclaredArtifactsWithClasses:
+
+UnusedDeclaredArtifacts:
+
+TestArtifactsWithNonTestScope:
+'''
+
+assert analysis == expected
diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java
index 4af2ff4..e7b52d3 100644
--- a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java
+++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java
@@ -48,8 +48,8 @@ public CollectorClassFileVisitor(ClassesPatterns excludedClasses) {
/** {@inheritDoc} */
@Override
public void visitClass(String className, InputStream in) {
- // inner classes have equivalent compilation requirement as container class
- if (className.indexOf('$') < 0 && !excludedClasses.isMatch(className)) {
+ // '$' is legal in a top-level class name, so nesting cannot be inferred from the binary name.
+ if (!excludedClasses.isMatch(className)) {
classes.add(className);
}
}
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 65e3937..e416531 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
@@ -108,10 +108,8 @@ void addType(final String usedByClass, final Type t) {
* @param name a {@link java.lang.String} object.
*/
public void add(final String usedByClass, final String name) {
- // inner classes have equivalent compilation requirement as container class
- if (name.indexOf('$') < 0) {
- classUsages.add(new DependencyUsage(name, usedByClass));
- }
+ // '$' is legal in a top-level class name, so nesting cannot be inferred from the binary name.
+ classUsages.add(new DependencyUsage(name, usedByClass));
}
void addNames(final String usedByClass, final String[] names) {
diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitorTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitorTest.java
index 9ae1faf..015dfc8 100644
--- a/src/test/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitorTest.java
+++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitorTest.java
@@ -51,4 +51,12 @@ void testVisitClass() {
assertThat(visitor.getClasses()).isEqualTo(expected);
}
+
+ @Test
+ void testVisitClassesContainingDollar() {
+ visitor.visitClass("a.b.Outer$Inner", null);
+ visitor.visitClass("x.y.Outer$Inner$Nested", null);
+
+ assertThat(visitor.getClasses()).containsExactlyInAnyOrder("a.b.Outer$Inner", "x.y.Outer$Inner$Nested");
+ }
}
diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzerTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzerTest.java
index 5edb864..96f9ab9 100644
--- a/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzerTest.java
+++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzerTest.java
@@ -24,6 +24,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarOutputStream;
@@ -91,6 +92,20 @@ void testAnalyzeBadJar() throws IOException {
}
}
+ @Test
+ void testAnalyzeDirectoryPreservesDollarClassNames() throws IOException {
+ Path directory = Files.createDirectories(tempDir.resolve("classes/library"));
+ Files.write(directory.resolve("Dollar$Class.class"), new byte[0]);
+ Files.write(directory.resolve("Outer$Inner.class"), new byte[0]);
+
+ DefaultClassAnalyzer analyzer = new DefaultClassAnalyzer();
+ Set actualClasses = analyzer.analyze(
+ tempDir.resolve("classes").toUri().toURL(),
+ new ClassesPatterns(Collections.singleton("library\\.Outer\\$Inner")));
+
+ assertThat(actualClasses).containsExactly("library.Dollar$Class");
+ }
+
private void addZipEntry(JarOutputStream out, String fileName, String content) throws IOException {
out.putNextEntry(new ZipEntry(fileName));
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java
index 562ab39..0fb7bd8 100644
--- a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java
+++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.maven.shared.dependency.analyzer.asm;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -31,6 +32,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -114,11 +118,30 @@ void testNoMethodHandle() throws IOException {
}
@Test
- void testInnerClassAsContainer() throws IOException {
+ void testInnerClassNamePreserved() throws IOException {
Set dependencies = getDependencies(InnerClassCase.class);
- for (String dependency : dependencies) {
- assertThat(dependency).doesNotContain("$");
- }
- assertThat(dependencies).contains("java.lang.System");
+ assertThat(dependencies).contains("java.lang.System", "java.lang.invoke.MethodHandles$Lookup");
+ }
+
+ @Test
+ void testInnerClassWithoutContainerClassInConstantPool() {
+ ClassWriter writer = new ClassWriter(0);
+ writer.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "consumer/Example", null, "java/lang/Object", null);
+
+ MethodVisitor method = writer.visitMethod(Opcodes.ACC_PUBLIC, "use", "()V", null, null);
+ method.visitCode();
+ method.visitTypeInsn(Opcodes.NEW, "dependency/Outer$Inner");
+ method.visitInsn(Opcodes.DUP);
+ method.visitMethodInsn(Opcodes.INVOKESPECIAL, "dependency/Outer$Inner", "", "()V", false);
+ method.visitInsn(Opcodes.POP);
+ method.visitInsn(Opcodes.RETURN);
+ method.visitMaxs(2, 1);
+ method.visitEnd();
+ writer.visitEnd();
+
+ DependencyClassFileVisitor visitor = new DependencyClassFileVisitor();
+ visitor.visitClass("consumer.Example", new ByteArrayInputStream(writer.toByteArray()));
+
+ assertThat(visitor.getDependencies()).contains("dependency.Outer$Inner").doesNotContain("dependency.Outer");
}
}