This blog outlines the high level approach to detect circular dependencies in a Java Project.
Program Goal :
Detecting circular dependencies
Input :
Path to Java project source files
Output :
Folder containing image files of cyclic graphs for each class having circular dependencies
Steps :
- Get a list of all the class names in a Java project
- Using Java Parser, create a Map of all the instance variables types belonging to a class. Filter the list of instance variable types against the list of class names.
- Use JgraphT to create a directed graph using the Map of class names to instance variables.
- Detect cycles for each vertex in the graph using JgraphT’s CycleDetector
- Store graphs as PNG files using jgrapht-ext library
Code Snippets
Collecting the Instance Variables of a Class using Java Parser
List instanceVarsTypes = compilationUnit
.findAll(FieldDeclaration.class)
.stream()
// get the Variable Declarator
.map(f -> f.getVariables().get(0).getType())
.filter(v -> !v.isPrimitiveType())
.map( v -> v.toString())
.filter(classNames::contains)
.collect(Collectors.toList());
Add To Map < Class Name , List Of Instance Variable Types >
Map> classNametoInstanceVarTypesMap = new HashMap<>();
if(!instanceVarsTypes.isEmpty()) {
String fileName = javaFile.getName();
String className = fileName.substring(0, fileName.lastIndexOf('.'));
classNametoInstanceVarTypesMap.put(className, instanceVarsTypes);
}
Create Directed Graph Using Map
Graph graph = new DefaultDirectedGraph<>(DefaultEdge.class);
//add vertices
classNametoInstanceVarTypesMap.keySet().forEach(className ->{
graph.addVertex(className);
});
//add edges
classNametoInstanceVarTypesMap
.forEach((className , instanceVariableTypes) -> {
instanceVariableTypes.forEach( instVar -> {
if (classNametoInstanceVarTypesMap.containsKey(instVar)){
graph.addEdge(className, instVar);
}
});
});
Detect Cycles For Each Vertex In The Graph Using JGraphT CycleDetector
CycleDetector cycleDetector = new CycleDetector(graph);
cycleDetector.findCycles().forEach(vertex -> {
AsSubgraph subGraph = new AsSubgraph<>(graph, cycleDetector.findCyclesContainingVertex(vertex));
createImage( subGraph, imageName);
});
findCyclesContainingVertex(vertex) returns set of vertices participating in the cycle for a vertex. Create a subgraph of the main graph using these vertices.
Create PNG Image Of A Graph
java File imgFile = new File(imageFilePath);
imgFile.createNewFile();
JGraphXAdapter graphAdapter = new JGraphXAdapter(subGraph);
mxIGraphLayout layout = new mxParallelEdgeLayout(graphAdapter); layout.execute(graphAdapter.getDefaultParent());
BufferedImage image = mxCellRenderer.createBufferedImage(graphAdapter, null, 2, Color.WHITE, true, null);
if(image != null) {
ImageIO.write(image, “PNG”, imgFile);
}