Skip to content

Commit cb5f997

Browse files
First attempt at capturing edge weights
1 parent 480aec4 commit cb5f997

File tree

9 files changed

+86
-44
lines changed

9 files changed

+86
-44
lines changed

circular-reference-detector/src/main/java/org/hjug/app/CircularReferenceDetectorApp.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import org.jgrapht.alg.flow.GusfieldGomoryHuCutTree;
1111
import org.jgrapht.graph.AsSubgraph;
1212
import org.jgrapht.graph.AsUndirectedGraph;
13-
import org.jgrapht.graph.DefaultEdge;
13+
import org.jgrapht.graph.DefaultWeightedEdge;
1414

1515
/**
1616
* Command line application to detect circular references in a java project.
@@ -41,7 +41,7 @@ public void launchApp(String[] args) {
4141
String outputDirectoryPath = args[1];
4242
JavaProjectParser javaProjectParser = new JavaProjectParser();
4343
try {
44-
Graph<String, DefaultEdge> classReferencesGraph =
44+
Graph<String, DefaultWeightedEdge> classReferencesGraph =
4545
javaProjectParser.getClassReferences(srcDirectoryPath);
4646
detectAndStoreCyclesInDirectory(outputDirectoryPath, classReferencesGraph);
4747
} catch (Exception e) {
@@ -51,9 +51,9 @@ public void launchApp(String[] args) {
5151
}
5252

5353
private void detectAndStoreCyclesInDirectory(
54-
String outputDirectoryPath, Graph<String, DefaultEdge> classReferencesGraph) {
54+
String outputDirectoryPath, Graph<String, DefaultWeightedEdge> classReferencesGraph) {
5555
CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker();
56-
Map<String, AsSubgraph<String, DefaultEdge>> cyclesForEveryVertexMap =
56+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cyclesForEveryVertexMap =
5757
circularReferenceChecker.detectCycles(classReferencesGraph);
5858
cyclesForEveryVertexMap.forEach((vertex, subGraph) -> {
5959
try {
@@ -64,13 +64,13 @@ private void detectAndStoreCyclesInDirectory(
6464
renderedSubGraphs.put(vertex, subGraph);
6565
System.out.println(
6666
"Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount);
67-
GusfieldGomoryHuCutTree<String, DefaultEdge> gusfieldGomoryHuCutTree =
67+
GusfieldGomoryHuCutTree<String, DefaultWeightedEdge> gusfieldGomoryHuCutTree =
6868
new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph));
6969
double minCut = gusfieldGomoryHuCutTree.calculateMinCut();
7070
System.out.println("Min cut weight: " + minCut);
71-
Set<DefaultEdge> minCutEdges = gusfieldGomoryHuCutTree.getCutEdges();
71+
Set<DefaultWeightedEdge> minCutEdges = gusfieldGomoryHuCutTree.getCutEdges();
7272
System.out.println("Minimum Cut Edges:");
73-
for (DefaultEdge minCutEdge : minCutEdges) {
73+
for (DefaultWeightedEdge minCutEdge : minCutEdges) {
7474
System.out.println(minCutEdge);
7575
}
7676
}
@@ -80,7 +80,7 @@ private void detectAndStoreCyclesInDirectory(
8080
});
8181
}
8282

83-
private boolean isDuplicateSubGraph(AsSubgraph<String, DefaultEdge> subGraph, String vertex) {
83+
private boolean isDuplicateSubGraph(AsSubgraph<String, DefaultWeightedEdge> subGraph, String vertex) {
8484
if (!renderedSubGraphs.isEmpty()) {
8585
for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) {
8686
if (renderedSubGraph.vertexSet().size() == subGraph.vertexSet().size()

circular-reference-detector/src/main/java/org/hjug/cycledetector/CircularReferenceChecker.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import org.jgrapht.alg.cycle.CycleDetector;
1515
import org.jgrapht.ext.JGraphXAdapter;
1616
import org.jgrapht.graph.AsSubgraph;
17-
import org.jgrapht.graph.DefaultEdge;
17+
import org.jgrapht.graph.DefaultWeightedEdge;
1818

1919
public class CircularReferenceChecker {
2020

@@ -25,11 +25,12 @@ public class CircularReferenceChecker {
2525
* @param classReferencesGraph
2626
* @return a Map of Class and its Cycle Graph
2727
*/
28-
public Map<String, AsSubgraph<String, DefaultEdge>> detectCycles(Graph<String, DefaultEdge> classReferencesGraph) {
29-
Map<String, AsSubgraph<String, DefaultEdge>> cyclesForEveryVertexMap = new HashMap<>();
30-
CycleDetector<String, DefaultEdge> cycleDetector = new CycleDetector<>(classReferencesGraph);
28+
public Map<String, AsSubgraph<String, DefaultWeightedEdge>> detectCycles(
29+
Graph<String, DefaultWeightedEdge> classReferencesGraph) {
30+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cyclesForEveryVertexMap = new HashMap<>();
31+
CycleDetector<String, DefaultWeightedEdge> cycleDetector = new CycleDetector<>(classReferencesGraph);
3132
cycleDetector.findCycles().forEach(v -> {
32-
AsSubgraph<String, DefaultEdge> subGraph =
33+
AsSubgraph<String, DefaultWeightedEdge> subGraph =
3334
new AsSubgraph<>(classReferencesGraph, cycleDetector.findCyclesContainingVertex(v));
3435
cyclesForEveryVertexMap.put(v, subGraph);
3536
});
@@ -45,12 +46,12 @@ public Map<String, AsSubgraph<String, DefaultEdge>> detectCycles(Graph<String, D
4546
* @param imageName
4647
* @throws IOException
4748
*/
48-
public void createImage(String outputDirectoryPath, Graph<String, DefaultEdge> subGraph, String imageName)
49+
public void createImage(String outputDirectoryPath, Graph<String, DefaultWeightedEdge> subGraph, String imageName)
4950
throws IOException {
5051
new File(outputDirectoryPath).mkdirs();
5152
File imgFile = new File(outputDirectoryPath + "/graph" + imageName + ".png");
5253
if (imgFile.createNewFile()) {
53-
JGraphXAdapter<String, DefaultEdge> graphAdapter = new JGraphXAdapter<>(subGraph);
54+
JGraphXAdapter<String, DefaultWeightedEdge> graphAdapter = new JGraphXAdapter<>(subGraph);
5455
mxIGraphLayout layout = new mxCircleLayout(graphAdapter);
5556
layout.execute(graphAdapter.getDefaultParent());
5657

circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import java.util.stream.Collectors;
1616
import java.util.stream.Stream;
1717
import org.jgrapht.Graph;
18-
import org.jgrapht.graph.DefaultDirectedGraph;
19-
import org.jgrapht.graph.DefaultEdge;
18+
import org.jgrapht.graph.DefaultWeightedEdge;
19+
import org.jgrapht.graph.builder.GraphTypeBuilder;
2020

2121
public class JavaProjectParser {
2222

@@ -26,8 +26,14 @@ public class JavaProjectParser {
2626
* @return
2727
* @throws IOException
2828
*/
29-
public Graph<String, DefaultEdge> getClassReferences(String srcDirectory) throws IOException {
30-
Graph<String, DefaultEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
29+
public Graph<String, DefaultWeightedEdge> getClassReferences(String srcDirectory) throws IOException {
30+
Graph<String, DefaultWeightedEdge> classReferencesGraph =
31+
GraphTypeBuilder.<String, DefaultWeightedEdge>directed()
32+
.allowingMultipleEdges(false)
33+
.allowingSelfLoops(true)
34+
.edgeClass(DefaultWeightedEdge.class)
35+
.weighted(true)
36+
.buildGraph();
3137
if (srcDirectory == null || srcDirectory.isEmpty()) {
3238
throw new IllegalArgumentException();
3339
} else {
@@ -43,13 +49,26 @@ public Graph<String, DefaultEdge> getClassReferences(String srcDirectory) throws
4349
getClassName(path.getFileName().toString());
4450
classReferencesGraph.addVertex(className);
4551
varTypes.forEach(classReferencesGraph::addVertex);
46-
varTypes.forEach(var -> classReferencesGraph.addEdge(className, var));
52+
varTypes.forEach(varType -> {
53+
DefaultWeightedEdge weightedEdge = classReferencesGraph.addEdge(className, varType);
54+
55+
// not sure why some edges are null, but let's ignore them for now
56+
if (null != weightedEdge) {
57+
classReferencesGraph.setEdgeWeight(
58+
weightedEdge, classReferencesGraph.getEdgeWeight(weightedEdge) + 1);
59+
}
60+
});
4761
}
4862
});
4963
} catch (FileNotFoundException e) {
5064
e.printStackTrace();
5165
}
5266
}
67+
68+
for (DefaultWeightedEdge weightedEdge : classReferencesGraph.edgeSet()) {
69+
System.out.println(weightedEdge.toString() + ":" + classReferencesGraph.getEdgeWeight(weightedEdge));
70+
}
71+
5372
return classReferencesGraph;
5473
}
5574

circular-reference-detector/src/test/java/org/hjug/cycledetector/CircularReferenceCheckerTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import org.jgrapht.Graph;
1010
import org.jgrapht.graph.AsSubgraph;
1111
import org.jgrapht.graph.DefaultDirectedGraph;
12-
import org.jgrapht.graph.DefaultEdge;
12+
import org.jgrapht.graph.DefaultWeightedEdge;
1313
import org.junit.jupiter.api.DisplayName;
1414
import org.junit.jupiter.api.Test;
1515

@@ -20,22 +20,22 @@ class CircularReferenceCheckerTests {
2020
@DisplayName("Detect 3 cycles from given graph.")
2121
@Test
2222
public void detectCyclesTest() {
23-
Graph<String, DefaultEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
23+
Graph<String, DefaultWeightedEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultWeightedEdge.class);
2424
classReferencesGraph.addVertex("A");
2525
classReferencesGraph.addVertex("B");
2626
classReferencesGraph.addVertex("C");
2727
classReferencesGraph.addEdge("A", "B");
2828
classReferencesGraph.addEdge("B", "C");
2929
classReferencesGraph.addEdge("C", "A");
30-
Map<String, AsSubgraph<String, DefaultEdge>> cyclesForEveryVertexMap =
30+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cyclesForEveryVertexMap =
3131
sutCircularReferenceChecker.detectCycles(classReferencesGraph);
3232
assertEquals(3, cyclesForEveryVertexMap.size());
3333
}
3434

3535
@DisplayName("Create graph image in given outputDirectory")
3636
@Test
3737
public void createImageTest() throws IOException {
38-
Graph<String, DefaultEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
38+
Graph<String, DefaultWeightedEdge> classReferencesGraph = new DefaultDirectedGraph<>(DefaultWeightedEdge.class);
3939
classReferencesGraph.addVertex("A");
4040
classReferencesGraph.addVertex("B");
4141
classReferencesGraph.addVertex("C");

circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.io.File;
66
import java.io.IOException;
77
import org.jgrapht.Graph;
8-
import org.jgrapht.graph.DefaultEdge;
8+
import org.jgrapht.graph.DefaultWeightedEdge;
99
import org.junit.jupiter.api.Assertions;
1010
import org.junit.jupiter.api.DisplayName;
1111
import org.junit.jupiter.api.Test;
@@ -25,7 +25,7 @@ public void parseSourceDirectoryEmptyTest() {
2525
@Test
2626
public void parseSourceDirectoryTest() throws IOException {
2727
File srcDirectory = new File("src/test/resources/javaSrcDirectory");
28-
Graph<String, DefaultEdge> classReferencesGraph =
28+
Graph<String, DefaultWeightedEdge> classReferencesGraph =
2929
sutJavaProjectParser.getClassReferences(srcDirectory.getAbsolutePath());
3030
assertNotNull(classReferencesGraph);
3131
assertEquals(5, classReferencesGraph.vertexSet().size());
@@ -42,5 +42,7 @@ public void parseSourceDirectoryTest() throws IOException {
4242
assertTrue(classReferencesGraph.containsEdge("D", "A"));
4343
assertTrue(classReferencesGraph.containsEdge("D", "C"));
4444
assertTrue(classReferencesGraph.containsEdge("E", "D"));
45+
DefaultWeightedEdge edge = classReferencesGraph.getEdge("E", "D");
46+
assertEquals(2, classReferencesGraph.getEdgeWeight(edge));
4547
}
4648
}

circular-reference-detector/src/test/resources/javaSrcDirectory/com/ideacrest/parser/testclasses/E.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
public class E {
44
D d;
5+
D d2;
56
}

cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.*;
1414
import java.util.stream.Collectors;
1515
import java.util.stream.Stream;
16+
import lombok.Getter;
1617
import lombok.extern.slf4j.Slf4j;
1718
import net.sourceforge.pmd.*;
1819
import net.sourceforge.pmd.lang.LanguageRegistry;
@@ -29,7 +30,7 @@
2930
import org.jgrapht.alg.flow.GusfieldGomoryHuCutTree;
3031
import org.jgrapht.graph.AsSubgraph;
3132
import org.jgrapht.graph.AsUndirectedGraph;
32-
import org.jgrapht.graph.DefaultEdge;
33+
import org.jgrapht.graph.DefaultWeightedEdge;
3334

3435
@Slf4j
3536
public class CostBenefitCalculator {
@@ -43,6 +44,9 @@ public class CostBenefitCalculator {
4344
private final ChangePronenessRanker changePronenessRanker;
4445
private final JavaProjectParser javaProjectParser = new JavaProjectParser();
4546

47+
@Getter
48+
private Graph<String, DefaultWeightedEdge> classReferencesGraph;
49+
4650
public CostBenefitCalculator(String repositoryPath) {
4751
this.repositoryPath = repositoryPath;
4852

@@ -65,15 +69,15 @@ public List<RankedCycle> runCycleAnalysis(String outputDirectoryPath, boolean re
6569
List<RankedCycle> rankedCycles = new ArrayList<>();
6670
try {
6771
Map<String, String> classNamesAndPaths = getClassNamesAndPaths();
68-
Graph<String, DefaultEdge> classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath);
72+
classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath);
6973
CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker();
70-
Map<String, AsSubgraph<String, DefaultEdge>> cyclesForEveryVertexMap =
74+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cyclesForEveryVertexMap =
7175
circularReferenceChecker.detectCycles(classReferencesGraph);
7276
cyclesForEveryVertexMap.forEach((vertex, subGraph) -> {
7377
int vertexCount = subGraph.vertexSet().size();
7478
int edgeCount = subGraph.edgeSet().size();
7579
double minCut = 0;
76-
Set<DefaultEdge> minCutEdges = null;
80+
Set<DefaultWeightedEdge> minCutEdges;
7781
if (vertexCount > 1 && edgeCount > 1 && !isDuplicateSubGraph(subGraph, vertex)) {
7882
if (renderImages) {
7983
try {
@@ -86,17 +90,24 @@ public List<RankedCycle> runCycleAnalysis(String outputDirectoryPath, boolean re
8690

8791
renderedSubGraphs.put(vertex, subGraph);
8892
log.info("Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount);
89-
GusfieldGomoryHuCutTree<String, DefaultEdge> gusfieldGomoryHuCutTree =
93+
GusfieldGomoryHuCutTree<String, DefaultWeightedEdge> gusfieldGomoryHuCutTree =
9094
new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph));
9195
minCut = gusfieldGomoryHuCutTree.calculateMinCut();
9296
log.info("Min cut weight: " + minCut);
9397
minCutEdges = gusfieldGomoryHuCutTree.getCutEdges();
9498

9599
log.info("Minimum Cut Edges:");
96-
for (DefaultEdge minCutEdge : minCutEdges) {
100+
for (DefaultWeightedEdge minCutEdge : minCutEdges) {
97101
log.info(minCutEdge.toString());
98102
}
99103

104+
log.info("All edge weights:");
105+
for (DefaultWeightedEdge weightedEdge :
106+
gusfieldGomoryHuCutTree.getGomoryHuTree().edgeSet()) {
107+
log.info(weightedEdge.toString() + ":"
108+
+ gusfieldGomoryHuCutTree.getGomoryHuTree().getEdgeWeight(weightedEdge));
109+
}
110+
100111
List<CycleNode> cycleNodes = subGraph.vertexSet().stream()
101112
.map(classInCycle -> new CycleNode(classInCycle, classNamesAndPaths.get(classInCycle)))
102113
.collect(Collectors.toList());
@@ -148,7 +159,7 @@ public List<RankedCycle> runCycleAnalysis(String outputDirectoryPath, boolean re
148159
return rankedCycles;
149160
}
150161

151-
private boolean isDuplicateSubGraph(AsSubgraph<String, DefaultEdge> subGraph, String vertex) {
162+
private boolean isDuplicateSubGraph(AsSubgraph<String, DefaultWeightedEdge> subGraph, String vertex) {
152163
if (!renderedSubGraphs.isEmpty()) {
153164
for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) {
154165
if (renderedSubGraph.vertexSet().size() == subGraph.vertexSet().size()

cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedCycle.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.util.Set;
66
import lombok.Data;
77
import lombok.extern.slf4j.Slf4j;
8-
import org.jgrapht.graph.DefaultEdge;
8+
import org.jgrapht.graph.DefaultWeightedEdge;
99

1010
@Data
1111
@Slf4j
@@ -15,9 +15,9 @@ public class RankedCycle {
1515
private final Integer changePronenessRankSum;
1616

1717
private final Set<String> vertexSet;
18-
private final Set<DefaultEdge> edgeSet;
18+
private final Set<DefaultWeightedEdge> edgeSet;
1919
private final double minCutCount;
20-
private final Set<DefaultEdge> minCutEdges;
20+
private final Set<DefaultWeightedEdge> minCutEdges;
2121
private final List<CycleNode> cycleNodes;
2222

2323
private float rawPriority;
@@ -30,9 +30,9 @@ public RankedCycle(
3030
String cycleName,
3131
Integer changePronenessRankSum,
3232
Set<String> vertexSet,
33-
Set<DefaultEdge> edgeSet,
33+
Set<DefaultWeightedEdge> edgeSet,
3434
double minCutCount,
35-
Set<DefaultEdge> minCutEdges,
35+
Set<DefaultWeightedEdge> minCutEdges,
3636
List<CycleNode> cycleNodes) {
3737
this.cycleNodes = cycleNodes;
3838
this.cycleName = cycleName;

report/src/main/java/org/hjug/refactorfirst/report/SimpleHtmlReport.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
import org.hjug.cbc.RankedCycle;
1818
import org.hjug.cbc.RankedDisharmony;
1919
import org.hjug.git.GitLogReader;
20-
import org.jgrapht.graph.DefaultEdge;
20+
import org.jgrapht.Graph;
21+
import org.jgrapht.graph.DefaultWeightedEdge;
2122

2223
/**
2324
* Strictly HTML report that contains no JavaScript
@@ -86,6 +87,8 @@ public class SimpleHtmlReport {
8687
// public final String[] classCycleTableHeadings = {"Classes", "Relationships", "Min Cut Edges"};
8788
public final String[] classCycleTableHeadings = {"Classes", "Relationships"};
8889

90+
private Graph<String, DefaultWeightedEdge> classGraph;
91+
8992
public void execute(
9093
boolean showDetails, String projectName, String projectVersion, String outputDirectory, File baseDir) {
9194

@@ -162,8 +165,9 @@ public void execute(
162165
List<RankedDisharmony> rankedCBODisharmonies = costBenefitCalculator.calculateCBOCostBenefitValues();
163166

164167
List<RankedCycle> rankedCycles = runCycleAnalysis(costBenefitCalculator, outputDirectory);
168+
classGraph = costBenefitCalculator.getClassReferencesGraph();
165169

166-
if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty()) {
170+
if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty() && rankedCycles.isEmpty()) {
167171
stringBuilder
168172
.append("Congratulations! ")
169173
.append(projectName)
@@ -257,8 +261,8 @@ private void renderCycles(
257261
stringBuilder.append("<tr>");
258262

259263
StringBuilder edgesToCut = new StringBuilder();
260-
for (DefaultEdge minCutEdge : rankedCycle.getMinCutEdges()) {
261-
edgesToCut.append(minCutEdge.toString());
264+
for (DefaultWeightedEdge minCutEdge : rankedCycle.getMinCutEdges()) {
265+
edgesToCut.append(minCutEdge + ":" + (int) classGraph.getEdgeWeight(minCutEdge));
262266
edgesToCut.append("</br>");
263267
}
264268

@@ -322,14 +326,18 @@ private void renderCycleTable(
322326
stringBuilder.append("<tr>");
323327
drawTableCell(vertex, stringBuilder);
324328
StringBuilder edges = new StringBuilder();
325-
for (org.jgrapht.graph.DefaultEdge edge : cycle.getEdgeSet()) {
329+
for (org.jgrapht.graph.DefaultWeightedEdge edge : cycle.getEdgeSet()) {
326330
if (edge.toString().startsWith("(" + vertex + " :")) {
327331
if (cycle.getMinCutEdges().contains(edge)) {
328332
edges.append("<strong>");
329-
edges.append(edge + "*");
333+
edges.append(edge);
334+
edges.append(":")
335+
.append((int) classGraph.getEdgeWeight(edge))
336+
.append("*");
330337
edges.append("</strong>");
331338
} else {
332339
edges.append(edge);
340+
edges.append(":").append((int) classGraph.getEdgeWeight(edge));
333341
}
334342

335343
edges.append("<br/>");

0 commit comments

Comments
 (0)