Skip to content
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
2 changes: 1 addition & 1 deletion codebase-graph-builder/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-recipe-bom</artifactId>
<version>3.4.0</version>
<version>3.33.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import lombok.extern.slf4j.Slf4j;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavadocVisitor;
import org.openrewrite.java.tree.*;
import org.openrewrite.java.tree.Javadoc;

@Slf4j
public class MetricsCollectingVisitor extends JavaIsoVisitor<ExecutionContext> {
Expand All @@ -22,6 +24,21 @@ public MetricsCollectingVisitor(MetricsCollector metricsCollector) {
this.metricsCollector = metricsCollector;
}

/**
* Returns a JavadocVisitor that does nothing. This is done to prevent the visitor from including references in
* Javadocs as metric counts
* @return JavadocVisitor that does nothing.
*/
@Override
protected JavadocVisitor<ExecutionContext> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<P> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, DefaultWeightedEdge> classGraph =
new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
DefaultDirectedWeightedGraph<String, DefaultWeightedEdge> packageGraph =
new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);

GraphMetricsCollector metricsCollector = new GraphMetricsCollector(classGraph, packageGraph);
MetricsCollectingVisitor metricsVisitor = new MetricsCollectingVisitor(metricsCollector);

List<Path> 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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.hjug.graphbuilder.metrics.testclasses.javadoc;

public class JavaDocServiceClass {
public String serviceName = "service";

public void serviceMethod() {}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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, "");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<String, DefaultWeightedEdge> 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<String, DefaultWeightedEdge> classReferencesGraph, String sourceVertex, String targetVertex) {
return classReferencesGraph.getEdgeWeight(classReferencesGraph.getEdge(sourceVertex, targetVertex));
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.hjug.graphbuilder.visitor.testclasses.javadoc;

public class JavaDocSibling {
public void publicMethod() {}
}
Loading