Skip to content

Commit 559631f

Browse files
Merge pull request #174 from refactorfirst/#173-process-lambda-bodes-and-inner-classes
#173 Processing lambda bodies and inner classes
2 parents a7e9fec + b939d8d commit 559631f

31 files changed

+1204
-512
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.hjug.graphbuilder;
2+
3+
public interface DependencyCollector {
4+
5+
/**
6+
* Records a dependency from one class to another
7+
*
8+
* @param fromClassFqn The fully qualified name of the class that depends on another
9+
* @param toClassFqn The fully qualified name of the class being depended upon
10+
*/
11+
void addClassDependency(String fromClassFqn, String toClassFqn);
12+
13+
/**
14+
* Records a dependency from one package to another
15+
*
16+
* @param fromPackageName The package that depends on another
17+
* @param toPackageName The package being depended upon
18+
*/
19+
void addPackageDependency(String fromPackageName, String toPackageName);
20+
21+
/**
22+
* Records the source file location for a class
23+
*
24+
* @param classFqn The fully qualified name of the class
25+
* @param sourceFilePath The path to the source file containing the class
26+
*/
27+
void recordClassLocation(String classFqn, String sourceFilePath);
28+
29+
/**
30+
* Registers a package as being part of the codebase
31+
*
32+
* @param packageName The package name to register
33+
*/
34+
void registerPackage(String packageName);
35+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.hjug.graphbuilder;
2+
3+
import lombok.Builder;
4+
import lombok.Value;
5+
6+
@Value
7+
@Builder
8+
public class GraphBuilderConfig {
9+
10+
@Builder.Default
11+
boolean excludeTests = true;
12+
13+
@Builder.Default
14+
String testSourceDirectory = "src/test";
15+
16+
public static GraphBuilderConfig defaultConfig() {
17+
return GraphBuilderConfig.builder().build();
18+
}
19+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.hjug.graphbuilder;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
import lombok.Getter;
6+
import org.jgrapht.Graph;
7+
import org.jgrapht.graph.DefaultWeightedEdge;
8+
9+
public class GraphDependencyCollector implements DependencyCollector {
10+
11+
@Getter
12+
private final Graph<String, DefaultWeightedEdge> classReferencesGraph;
13+
14+
@Getter
15+
private final Graph<String, DefaultWeightedEdge> packageReferencesGraph;
16+
17+
@Getter
18+
private final Set<String> packagesInCodebase = new HashSet<>();
19+
20+
public GraphDependencyCollector(
21+
Graph<String, DefaultWeightedEdge> classReferencesGraph,
22+
Graph<String, DefaultWeightedEdge> packageReferencesGraph) {
23+
this.classReferencesGraph = classReferencesGraph;
24+
this.packageReferencesGraph = packageReferencesGraph;
25+
}
26+
27+
@Override
28+
public void addClassDependency(String fromClassFqn, String toClassFqn) {
29+
if (fromClassFqn.equals(toClassFqn)) {
30+
return;
31+
}
32+
33+
classReferencesGraph.addVertex(fromClassFqn);
34+
classReferencesGraph.addVertex(toClassFqn);
35+
36+
if (!classReferencesGraph.containsEdge(fromClassFqn, toClassFqn)) {
37+
classReferencesGraph.addEdge(fromClassFqn, toClassFqn);
38+
} else {
39+
DefaultWeightedEdge edge = classReferencesGraph.getEdge(fromClassFqn, toClassFqn);
40+
classReferencesGraph.setEdgeWeight(edge, classReferencesGraph.getEdgeWeight(edge) + 1);
41+
}
42+
}
43+
44+
@Override
45+
public void addPackageDependency(String fromPackageName, String toPackageName) {
46+
if (fromPackageName.equals(toPackageName)) {
47+
return;
48+
}
49+
50+
packageReferencesGraph.addVertex(fromPackageName);
51+
packageReferencesGraph.addVertex(toPackageName);
52+
53+
if (!packageReferencesGraph.containsEdge(fromPackageName, toPackageName)) {
54+
packageReferencesGraph.addEdge(fromPackageName, toPackageName);
55+
} else {
56+
DefaultWeightedEdge edge = packageReferencesGraph.getEdge(fromPackageName, toPackageName);
57+
packageReferencesGraph.setEdgeWeight(edge, packageReferencesGraph.getEdgeWeight(edge) + 1);
58+
}
59+
}
60+
61+
@Override
62+
public void recordClassLocation(String classFqn, String sourceFilePath) {
63+
// This will be handled by JavaVisitor which maintains the mapping
64+
}
65+
66+
@Override
67+
public void registerPackage(String packageName) {
68+
packagesInCodebase.add(packageName);
69+
}
70+
}

codebase-graph-builder/src/main/java/org/hjug/graphbuilder/JavaGraphBuilder.java

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,49 +23,63 @@
2323
public class JavaGraphBuilder {
2424

2525
/**
26-
* Given a java source directory, return a CodebaseGraphDTO
26+
* Given a java source directory, return a CodebaseGraphDTO using default configuration
2727
*
28-
* @param srcDirectory
28+
* @param srcDirectory The source directory to analyze
29+
* @param excludeTests Whether to exclude test files
30+
* @param testSourceDirectory The test source directory pattern to exclude
2931
* @return CodebaseGraphDTO
3032
* @throws IOException
3133
*/
3234
public CodebaseGraphDTO getCodebaseGraphDTO(String srcDirectory, boolean excludeTests, String testSourceDirectory)
3335
throws IOException {
34-
CodebaseGraphDTO codebaseGraphDTO;
36+
GraphBuilderConfig config = GraphBuilderConfig.builder()
37+
.excludeTests(excludeTests)
38+
.testSourceDirectory(testSourceDirectory)
39+
.build();
40+
return getCodebaseGraphDTO(srcDirectory, config);
41+
}
42+
43+
/**
44+
* Given a java source directory and configuration, return a CodebaseGraphDTO
45+
*
46+
* @param srcDirectory The source directory to analyze
47+
* @param config The configuration for the graph builder
48+
* @return CodebaseGraphDTO
49+
* @throws IOException
50+
*/
51+
public CodebaseGraphDTO getCodebaseGraphDTO(String srcDirectory, GraphBuilderConfig config) throws IOException {
3552
if (srcDirectory == null || srcDirectory.isEmpty()) {
36-
throw new IllegalArgumentException();
37-
} else {
38-
codebaseGraphDTO = processWithOpenRewrite(srcDirectory, excludeTests, testSourceDirectory);
53+
throw new IllegalArgumentException("Source directory cannot be null or empty");
3954
}
40-
41-
return codebaseGraphDTO;
55+
return processWithOpenRewrite(srcDirectory, config);
4256
}
4357

44-
private CodebaseGraphDTO processWithOpenRewrite(String srcDir, boolean excludeTests, String testSourceDirectory)
45-
throws IOException {
58+
private CodebaseGraphDTO processWithOpenRewrite(String srcDir, GraphBuilderConfig config) throws IOException {
4659
File srcDirectory = new File(srcDir);
4760

4861
JavaParser javaParser = JavaParser.fromJavaVersion().build();
4962
ExecutionContext ctx = new InMemoryExecutionContext(Throwable::printStackTrace);
5063

5164
final Graph<String, DefaultWeightedEdge> classReferencesGraph =
5265
new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
53-
5466
final Graph<String, DefaultWeightedEdge> packageReferencesGraph =
5567
new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
5668

57-
final JavaVisitor<ExecutionContext> javaVisitor =
58-
new JavaVisitor<>(classReferencesGraph, packageReferencesGraph);
69+
final GraphDependencyCollector dependencyCollector =
70+
new GraphDependencyCollector(classReferencesGraph, packageReferencesGraph);
71+
72+
final JavaVisitor<ExecutionContext> javaVisitor = new JavaVisitor<>(dependencyCollector);
5973
final JavaVariableTypeVisitor<ExecutionContext> javaVariableTypeVisitor =
60-
new JavaVariableTypeVisitor<>(classReferencesGraph, packageReferencesGraph);
74+
new JavaVariableTypeVisitor<>(dependencyCollector);
6175
final JavaMethodDeclarationVisitor<ExecutionContext> javaMethodDeclarationVisitor =
62-
new JavaMethodDeclarationVisitor<>(classReferencesGraph, packageReferencesGraph);
76+
new JavaMethodDeclarationVisitor<>(dependencyCollector);
6377

6478
try (Stream<Path> pathStream = Files.walk(Paths.get(srcDirectory.getAbsolutePath()))) {
6579
List<Path> list;
66-
if (excludeTests) {
80+
if (config.isExcludeTests()) {
6781
list = pathStream
68-
.filter(file -> !file.toString().contains(testSourceDirectory))
82+
.filter(file -> !file.toString().contains(config.getTestSourceDirectory()))
6983
.collect(Collectors.toList());
7084
} else {
7185
list = pathStream.collect(Collectors.toList());
@@ -80,7 +94,7 @@ private CodebaseGraphDTO processWithOpenRewrite(String srcDir, boolean excludeTe
8094
});
8195
}
8296

83-
removeClassesNotInCodebase(javaVisitor.getPackagesInCodebase(), classReferencesGraph);
97+
removeClassesNotInCodebase(dependencyCollector.getPackagesInCodebase(), classReferencesGraph);
8498

8599
return new CodebaseGraphDTO(
86100
classReferencesGraph, packageReferencesGraph, javaVisitor.getClassToSourceFilePathMapping());
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.hjug.graphbuilder.visitor;
2+
3+
import lombok.Getter;
4+
import org.hjug.graphbuilder.DependencyCollector;
5+
import org.openrewrite.java.JavaIsoVisitor;
6+
7+
@Getter
8+
public abstract class BaseCodebaseVisitor<P> extends JavaIsoVisitor<P> {
9+
10+
protected final DependencyCollector dependencyCollector;
11+
12+
protected BaseCodebaseVisitor(DependencyCollector dependencyCollector) {
13+
this.dependencyCollector = dependencyCollector;
14+
}
15+
16+
protected abstract String getCurrentOwnerFqn();
17+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.hjug.graphbuilder.visitor;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.hjug.graphbuilder.DependencyCollector;
5+
import org.openrewrite.Cursor;
6+
import org.openrewrite.java.service.AnnotationService;
7+
import org.openrewrite.java.tree.Expression;
8+
import org.openrewrite.java.tree.J;
9+
import org.openrewrite.java.tree.JavaType;
10+
import org.openrewrite.java.tree.TypeTree;
11+
12+
@Slf4j
13+
public abstract class BaseTypeProcessor {
14+
15+
private final TypeDependencyExtractor typeDependencyExtractor = new TypeDependencyExtractor();
16+
17+
protected abstract DependencyCollector getDependencyCollector();
18+
19+
protected void processType(String ownerFqn, JavaType javaType) {
20+
if (javaType == null || javaType instanceof JavaType.Unknown) {
21+
return;
22+
}
23+
24+
for (String dependency : typeDependencyExtractor.extractDependencies(javaType)) {
25+
getDependencyCollector().addClassDependency(ownerFqn, dependency);
26+
}
27+
}
28+
29+
protected void processAnnotation(String ownerFqn, J.Annotation annotation, Cursor cursor) {
30+
if (annotation.getType() instanceof JavaType.Unknown) {
31+
return;
32+
}
33+
34+
JavaType.Class type = (JavaType.Class) annotation.getType();
35+
if (null != type) {
36+
String annotationFqn = type.getFullyQualifiedName();
37+
log.debug("Variable Annotation FQN: {}", annotationFqn);
38+
getDependencyCollector().addClassDependency(ownerFqn, annotationFqn);
39+
40+
if (null != annotation.getArguments()) {
41+
for (Expression argument : annotation.getArguments()) {
42+
processType(ownerFqn, argument.getType());
43+
}
44+
}
45+
}
46+
}
47+
48+
protected void processTypeParameter(String ownerFqn, J.TypeParameter typeParameter, Cursor cursor) {
49+
if (null != typeParameter.getBounds()) {
50+
for (TypeTree bound : typeParameter.getBounds()) {
51+
processType(ownerFqn, bound.getType());
52+
}
53+
}
54+
55+
if (!typeParameter.getAnnotations().isEmpty()) {
56+
for (J.Annotation annotation : typeParameter.getAnnotations()) {
57+
processAnnotation(ownerFqn, annotation, cursor);
58+
}
59+
}
60+
}
61+
62+
protected void processAnnotations(String ownerFqn, Cursor cursor) {
63+
AnnotationService annotationService = new AnnotationService();
64+
for (J.Annotation annotation : annotationService.getAllAnnotations(cursor)) {
65+
processAnnotation(ownerFqn, annotation, cursor);
66+
}
67+
}
68+
69+
protected String getPackageFromFqn(String fqn) {
70+
if (!fqn.contains(".")) {
71+
return "";
72+
}
73+
int lastIndex = fqn.lastIndexOf(".");
74+
return fqn.substring(0, lastIndex);
75+
}
76+
}

0 commit comments

Comments
 (0)