getJavadocVisitor() {
+ return new JavadocVisitor<>(this) {
+ @Override
+ public Javadoc visitDocComment(Javadoc.DocComment docComment, ExecutionContext ctx) {
+ return docComment;
+ }
+ };
+ }
+
@Override
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
currentSourcePath = cu.getSourcePath().toString(); // .toUri().toString();
diff --git a/codebase-graph-builder/src/main/java/org/hjug/graphbuilder/visitor/JavaVisitor.java b/codebase-graph-builder/src/main/java/org/hjug/graphbuilder/visitor/JavaVisitor.java
index 40a3edf..b30ac9e 100644
--- a/codebase-graph-builder/src/main/java/org/hjug/graphbuilder/visitor/JavaVisitor.java
+++ b/codebase-graph-builder/src/main/java/org/hjug/graphbuilder/visitor/JavaVisitor.java
@@ -5,7 +5,9 @@
import lombok.extern.slf4j.Slf4j;
import org.hjug.graphbuilder.DependencyCollector;
import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavadocVisitor;
import org.openrewrite.java.tree.*;
+import org.openrewrite.java.tree.Javadoc;
/**
* BUG: Static method calls and definitions are not being captured, but were previously being captured.
@@ -38,6 +40,21 @@ protected DependencyCollector getDependencyCollector() {
};
}
+ /**
+ * Returns a JavadocVisitor that does nothing. This is done to prevent the visitor from including references in
+ * Javadocs as members of cycles
+ * @return JavadocVisitor that does nothing.
+ */
+ @Override
+ protected JavadocVisitor getJavadocVisitor() {
+ return new JavadocVisitor<>(this) {
+ @Override
+ public Javadoc visitDocComment(Javadoc.DocComment docComment, P p) {
+ return docComment;
+ }
+ };
+ }
+
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P p) {
JavaType.FullyQualified type = classDecl.getType();
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/MetricsCollectionTest.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/MetricsCollectionTest.java
index b4085c4..9916380 100644
--- a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/MetricsCollectionTest.java
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/MetricsCollectionTest.java
@@ -851,4 +851,37 @@ void sourceFilePathCapturedForAllClasses() throws IOException {
"sourceFilePath should not be null for " + classMetrics.getFullyQualifiedName());
}
}
+
+ @Test
+ void javadocMethodReferenceIsNotCountedAsForeignMethodCall() throws IOException {
+ File srcDirectory = new File("src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc");
+
+ JavaParser javaParser = JavaParser.fromJavaVersion().build();
+ ExecutionContext ctx = new InMemoryExecutionContext(Throwable::printStackTrace);
+
+ DefaultDirectedWeightedGraph classGraph =
+ new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
+ DefaultDirectedWeightedGraph packageGraph =
+ new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
+
+ GraphMetricsCollector metricsCollector = new GraphMetricsCollector(classGraph, packageGraph);
+ MetricsCollectingVisitor metricsVisitor = new MetricsCollectingVisitor(metricsCollector);
+
+ List list = Files.walk(Paths.get(srcDirectory.getAbsolutePath())).collect(Collectors.toList());
+ javaParser
+ .parse(list, Paths.get(srcDirectory.getAbsolutePath()), ctx)
+ .forEach(cu -> metricsVisitor.visit(cu, ctx));
+
+ metricsCollector.finalizeMetrics();
+
+ ClassMetrics metrics = metricsCollector.getClassMetrics(
+ "org.hjug.graphbuilder.metrics.testclasses.javadoc.MethodWithJavadocReference");
+ Assertions.assertNotNull(metrics, "MethodWithJavadocReference should be collected");
+
+ Assertions.assertEquals(
+ 0,
+ metrics.getCouplingBetweenObjects(),
+ "CBO must be 0: Semaphore.acquire() is Javadoc-only and must not be counted as a foreign dependency. Got: "
+ + metrics.getCouplingBetweenObjects());
+ }
}
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/JavaDocServiceClass.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/JavaDocServiceClass.java
new file mode 100644
index 0000000..886116f
--- /dev/null
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/JavaDocServiceClass.java
@@ -0,0 +1,7 @@
+package org.hjug.graphbuilder.metrics.testclasses.javadoc;
+
+public class JavaDocServiceClass {
+ public String serviceName = "service";
+
+ public void serviceMethod() {}
+}
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/MethodWithJavadocReference.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/MethodWithJavadocReference.java
new file mode 100644
index 0000000..17f9f5a
--- /dev/null
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/metrics/testclasses/javadoc/MethodWithJavadocReference.java
@@ -0,0 +1,15 @@
+package org.hjug.graphbuilder.metrics.testclasses.javadoc;
+
+/**
+ * Class whose methods reference external fields only in Javadoc.
+ */
+public class MethodWithJavadocReference {
+
+ /**
+ * Does nothing in its body.
+ * See {@link JavaDocServiceClass#serviceName} for context.
+ */
+ public void doSomething() {
+ // intentionally empty - JavaDocServiceClass.serviceName is not accessed here
+ }
+}
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/JavaVisitorTest.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/JavaVisitorTest.java
index 160a413..e05df53 100644
--- a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/JavaVisitorTest.java
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/JavaVisitorTest.java
@@ -33,6 +33,7 @@ class JavaVisitorTest {
private static final String INITIALIZERS = TESTCLASSES + "/initializers";
private static final String VARIABLE_INITIALIZERS = TESTCLASSES + "/variableInitializers";
private static final String TRY_CATCH = TESTCLASSES + "/tryCatch";
+ private static final String JAVADOC_TESTCLASSES = TESTCLASSES + "/javadoc";
private static String repoFrom(String pathString) {
return new File(pathString).toURI().toString().replace("/" + pathString, "");
@@ -84,7 +85,7 @@ void visitClasses_registersExpectedPackageCount() throws IOException {
javaParser
.parse(list, Paths.get(srcDirectory.getAbsolutePath()), ctx)
.forEach(cu -> javaVisitor.visit(cu, ctx));
- assertEquals(7, dependencyCollector.getPackagesInCodebase().size());
+ assertEquals(8, dependencyCollector.getPackagesInCodebase().size());
}
@Test
@@ -361,6 +362,16 @@ void catchClauseTypeIsCountedOnce() throws IOException {
"catch clause exception type is double-processed by visitTry manual loop after super already handled it");
}
+ @Test
+ void javadocReferencedClassDoesNotCreateSpuriousDependencyEdge() throws IOException {
+ Graph graph = buildAndVisit(JAVADOC_TESTCLASSES);
+ assertFalse(
+ graph.containsEdge(
+ "org.hjug.graphbuilder.visitor.testclasses.javadoc.JavaDocOwner",
+ "org.hjug.graphbuilder.visitor.testclasses.javadoc.JavaDocSibling"),
+ "JavaDocSibling is referenced only in Javadoc {@link} and must not create a dependency edge from JavaDocOwner");
+ }
+
private static double getEdgeWeight(
Graph classReferencesGraph, String sourceVertex, String targetVertex) {
return classReferencesGraph.getEdgeWeight(classReferencesGraph.getEdge(sourceVertex, targetVertex));
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocOwner.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocOwner.java
new file mode 100644
index 0000000..5c51c33
--- /dev/null
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocOwner.java
@@ -0,0 +1,21 @@
+package org.hjug.graphbuilder.visitor.testclasses.javadoc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that references {@link JavaDocSibling} only in documentation.
+ */
+public class JavaDocOwner {
+ private List items = new ArrayList<>();
+
+ /**
+ * Does something unrelated to JavaDocSibling.
+ * See also {@link JavaDocSibling#publicMethod()} for a related operation.
+ *
+ * @param name the name
+ */
+ public void doSomething(String name) {
+ items.add(name);
+ }
+}
diff --git a/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocSibling.java b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocSibling.java
new file mode 100644
index 0000000..62c5244
--- /dev/null
+++ b/codebase-graph-builder/src/test/java/org/hjug/graphbuilder/visitor/testclasses/javadoc/JavaDocSibling.java
@@ -0,0 +1,5 @@
+package org.hjug.graphbuilder.visitor.testclasses.javadoc;
+
+public class JavaDocSibling {
+ public void publicMethod() {}
+}