Skip to content

Commit 691e632

Browse files
Merge pull request #106 from refactorfirst/optionally-calculate-commit-count-of-cycle-classes
#93 Only calculating cycle class churn when `showDetails = true`
2 parents 827e170 + c786fbb commit 691e632

File tree

4 files changed

+178
-81
lines changed

4 files changed

+178
-81
lines changed

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

Lines changed: 106 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -73,73 +73,125 @@ public List<RankedCycle> runCycleAnalysis() {
7373
StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.BLEEDING_EDGE);
7474
List<RankedCycle> rankedCycles = new ArrayList<>();
7575
try {
76-
Map<String, String> classNamesAndPaths = getClassNamesAndPaths();
77-
classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath);
78-
CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker();
79-
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cyclesForEveryVertexMap =
80-
circularReferenceChecker.detectCycles(classReferencesGraph);
81-
cyclesForEveryVertexMap.forEach((vertex, subGraph) -> {
82-
int vertexCount = subGraph.vertexSet().size();
83-
int edgeCount = subGraph.edgeSet().size();
84-
double minCut = 0;
85-
Set<DefaultWeightedEdge> minCutEdges;
86-
if (vertexCount > 1 && edgeCount > 1 && !isDuplicateSubGraph(subGraph, vertex)) {
87-
renderedSubGraphs.put(vertex, subGraph);
88-
log.info("Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount);
89-
GusfieldGomoryHuCutTree<String, DefaultWeightedEdge> gusfieldGomoryHuCutTree =
90-
new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph));
91-
minCut = gusfieldGomoryHuCutTree.calculateMinCut();
92-
minCutEdges = gusfieldGomoryHuCutTree.getCutEdges();
93-
94-
List<CycleNode> cycleNodes = subGraph.vertexSet().stream()
95-
.map(classInCycle -> new CycleNode(classInCycle, classNamesAndPaths.get(classInCycle)))
96-
.collect(Collectors.toList());
97-
List<ScmLogInfo> changeRanks = getRankedChangeProneness(cycleNodes);
98-
99-
Map<String, CycleNode> cycleNodeMap = new HashMap<>();
100-
101-
for (CycleNode cycleNode : cycleNodes) {
102-
cycleNodeMap.put(cycleNode.getFileName(), cycleNode);
103-
}
76+
boolean calculateCycleChurn = false;
77+
idenfifyRankedCycles(rankedCycles, calculateCycleChurn);
78+
sortRankedCycles(rankedCycles, calculateCycleChurn);
79+
setPriorities(rankedCycles);
80+
} catch (IOException e) {
81+
throw new RuntimeException(e);
82+
}
10483

105-
for (ScmLogInfo changeRank : changeRanks) {
106-
CycleNode cycleNode = cycleNodeMap.get(changeRank.getPath());
107-
cycleNode.setScmLogInfo(changeRank);
108-
}
84+
return rankedCycles;
85+
}
10986

110-
// sum change proneness ranks
111-
int changePronenessRankSum = changeRanks.stream()
112-
.mapToInt(ScmLogInfo::getChangePronenessRank)
113-
.sum();
114-
rankedCycles.add(new RankedCycle(
115-
vertex,
116-
changePronenessRankSum,
117-
subGraph.vertexSet(),
118-
subGraph.edgeSet(),
119-
minCut,
120-
minCutEdges,
121-
cycleNodes));
122-
}
123-
});
87+
public List<RankedCycle> runCycleAnalysisAndCalculateCycleChurn() {
88+
StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.BLEEDING_EDGE);
89+
List<RankedCycle> rankedCycles = new ArrayList<>();
90+
try {
91+
boolean calculateCycleChurn = true;
92+
idenfifyRankedCycles(rankedCycles, calculateCycleChurn);
93+
sortRankedCycles(rankedCycles, calculateCycleChurn);
94+
setPriorities(rankedCycles);
95+
} catch (IOException e) {
96+
throw new RuntimeException(e);
97+
}
12498

99+
return rankedCycles;
100+
}
101+
102+
private void idenfifyRankedCycles(List<RankedCycle> rankedCycles, boolean calculateChurnForCycles)
103+
throws IOException {
104+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cycles = getCycles();
105+
Map<String, String> classNamesAndPaths = getClassNamesAndPaths();
106+
cycles.forEach((vertex, subGraph) -> {
107+
int vertexCount = subGraph.vertexSet().size();
108+
int edgeCount = subGraph.edgeSet().size();
109+
110+
if (vertexCount > 1 && edgeCount > 1 && !isDuplicateSubGraph(subGraph, vertex)) {
111+
renderedSubGraphs.put(vertex, subGraph);
112+
log.info("Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount);
113+
GusfieldGomoryHuCutTree<String, DefaultWeightedEdge> gusfieldGomoryHuCutTree =
114+
new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph));
115+
double minCut = gusfieldGomoryHuCutTree.calculateMinCut();
116+
Set<DefaultWeightedEdge> minCutEdges = gusfieldGomoryHuCutTree.getCutEdges();
117+
118+
List<CycleNode> cycleNodes = subGraph.vertexSet().stream()
119+
.map(classInCycle -> new CycleNode(classInCycle, classNamesAndPaths.get(classInCycle)))
120+
.collect(Collectors.toList());
121+
122+
rankedCycles.add(
123+
createRankedCycle(calculateChurnForCycles, vertex, subGraph, cycleNodes, minCut, minCutEdges));
124+
}
125+
});
126+
}
127+
128+
private static void setPriorities(List<RankedCycle> rankedCycles) {
129+
int priority = 1;
130+
for (RankedCycle rankedCycle : rankedCycles) {
131+
rankedCycle.setPriority(priority++);
132+
}
133+
}
134+
135+
private Map<String, AsSubgraph<String, DefaultWeightedEdge>> getCycles() throws IOException {
136+
classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath);
137+
CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker();
138+
Map<String, AsSubgraph<String, DefaultWeightedEdge>> cycles =
139+
circularReferenceChecker.detectCycles(classReferencesGraph);
140+
return cycles;
141+
}
142+
143+
private static void sortRankedCycles(List<RankedCycle> rankedCycles, boolean calculateChurnForCycles) {
144+
if (calculateChurnForCycles) {
125145
rankedCycles.sort(Comparator.comparing(RankedCycle::getAverageChangeProneness));
146+
126147
int cpr = 1;
127148
for (RankedCycle rankedCycle : rankedCycles) {
128149
rankedCycle.setChangePronenessRank(cpr++);
129150
}
130-
151+
} else {
131152
rankedCycles.sort(Comparator.comparing(RankedCycle::getRawPriority).reversed());
153+
}
154+
}
132155

133-
int priority = 1;
134-
for (RankedCycle rankedCycle : rankedCycles) {
135-
rankedCycle.setPriority(priority++);
156+
private RankedCycle createRankedCycle(
157+
boolean calculateChurnForCycles,
158+
String vertex,
159+
AsSubgraph<String, DefaultWeightedEdge> subGraph,
160+
List<CycleNode> cycleNodes,
161+
double minCut,
162+
Set<DefaultWeightedEdge> minCutEdges) {
163+
RankedCycle rankedCycle;
164+
if (calculateChurnForCycles) {
165+
List<ScmLogInfo> changeRanks = getRankedChangeProneness(cycleNodes);
166+
167+
Map<String, CycleNode> cycleNodeMap = new HashMap<>();
168+
169+
for (CycleNode cycleNode : cycleNodes) {
170+
cycleNodeMap.put(cycleNode.getFileName(), cycleNode);
136171
}
137172

138-
} catch (IOException e) {
139-
throw new RuntimeException(e);
140-
}
173+
for (ScmLogInfo changeRank : changeRanks) {
174+
CycleNode cycleNode = cycleNodeMap.get(changeRank.getPath());
175+
cycleNode.setScmLogInfo(changeRank);
176+
}
141177

142-
return rankedCycles;
178+
// sum change proneness ranks
179+
int changePronenessRankSum = changeRanks.stream()
180+
.mapToInt(ScmLogInfo::getChangePronenessRank)
181+
.sum();
182+
rankedCycle = new RankedCycle(
183+
vertex,
184+
changePronenessRankSum,
185+
subGraph.vertexSet(),
186+
subGraph.edgeSet(),
187+
minCut,
188+
minCutEdges,
189+
cycleNodes);
190+
} else {
191+
rankedCycle =
192+
new RankedCycle(vertex, subGraph.vertexSet(), subGraph.edgeSet(), minCut, minCutEdges, cycleNodes);
193+
}
194+
return rankedCycle;
143195
}
144196

145197
private boolean isDuplicateSubGraph(AsSubgraph<String, DefaultWeightedEdge> subGraph, String vertex) {

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
public class RankedCycle {
1313

1414
private final String cycleName;
15-
private final Integer changePronenessRankSum;
15+
private Integer changePronenessRankSum = 0;
1616

1717
private final Set<String> vertexSet;
1818
private final Set<DefaultWeightedEdge> edgeSet;
@@ -23,9 +23,37 @@ public class RankedCycle {
2323
private float rawPriority;
2424
private Integer priority = 0;
2525
private float averageChangeProneness;
26-
private Integer changePronenessRank;
26+
private Integer changePronenessRank = 0;
2727
private float impact;
2828

29+
public RankedCycle(
30+
String cycleName,
31+
Set<String> vertexSet,
32+
Set<DefaultWeightedEdge> edgeSet,
33+
double minCutCount,
34+
Set<DefaultWeightedEdge> minCutEdges,
35+
List<CycleNode> cycleNodes) {
36+
this.cycleNodes = cycleNodes;
37+
this.cycleName = cycleName;
38+
this.vertexSet = vertexSet;
39+
this.edgeSet = edgeSet;
40+
this.minCutCount = minCutCount;
41+
42+
if (null == minCutEdges) {
43+
this.minCutEdges = new HashSet<>();
44+
} else {
45+
this.minCutEdges = minCutEdges;
46+
}
47+
48+
if (minCutCount == 0.0) {
49+
this.impact = (float) (vertexSet.size());
50+
} else {
51+
this.impact = (float) (vertexSet.size() / minCutCount);
52+
}
53+
54+
this.rawPriority = this.impact;
55+
}
56+
2957
public RankedCycle(
3058
String cycleName,
3159
Integer changePronenessRankSum,

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.util.List;
88
import java.util.Locale;
99
import lombok.extern.slf4j.Slf4j;
10-
import org.hjug.cbc.CostBenefitCalculator;
1110
import org.hjug.cbc.RankedCycle;
1211
import org.hjug.cbc.RankedDisharmony;
1312
import org.hjug.gdg.GraphDataGenerator;
@@ -174,11 +173,6 @@ void renderCBOChart(
174173
stringBuilder.append(COUPLING_BETWEEN_OBJECT_CHART_LEGEND);
175174
}
176175

177-
@Override
178-
public List<RankedCycle> runCycleAnalysis(CostBenefitCalculator costBenefitCalculator, String outputDirectory) {
179-
return costBenefitCalculator.runCycleAnalysis();
180-
}
181-
182176
@Override
183177
public void renderCycleImage(
184178
Graph<String, DefaultWeightedEdge> classGraph, RankedCycle cycle, StringBuilder stringBuilder) {

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

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ public class SimpleHtmlReport {
6363
"Class", "Priority", "Change Proneness Rank", "Coupling Count", "Most Recent Commit Date", "Commit Count"
6464
};
6565

66-
public final String[] cycleTableHeadings = {
67-
"Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Minimum Cuts"
68-
};
69-
7066
public final String[] classCycleTableHeadings = {"Classes", "Relationships"};
7167

7268
private Graph<String, DefaultWeightedEdge> classGraph;
7369

70+
private boolean showDetails = false;
71+
7472
public void execute(
7573
boolean showDetails, String projectName, String projectVersion, String outputDirectory, File baseDir) {
7674

75+
this.showDetails = showDetails;
76+
7777
final String[] godClassTableHeadings =
7878
showDetails ? godClassDetailedTableHeadings : godClassSimpleTableHeadings;
7979

@@ -143,7 +143,12 @@ public void execute(
143143
costBenefitCalculator.runPmdAnalysis();
144144
rankedGodClassDisharmonies = costBenefitCalculator.calculateGodClassCostBenefitValues();
145145
rankedCBODisharmonies = costBenefitCalculator.calculateCBOCostBenefitValues();
146-
rankedCycles = runCycleAnalysis(costBenefitCalculator, outputDirectory);
146+
if (showDetails) {
147+
rankedCycles = costBenefitCalculator.runCycleAnalysisAndCalculateCycleChurn();
148+
} else {
149+
rankedCycles = costBenefitCalculator.runCycleAnalysis();
150+
}
151+
147152
classGraph = costBenefitCalculator.getClassReferencesGraph();
148153
} catch (Exception e) {
149154
log.error("Error running analysis.");
@@ -217,10 +222,6 @@ public void execute(
217222
log.info("Done! View the report at target/site/{}", filename);
218223
}
219224

220-
public List<RankedCycle> runCycleAnalysis(CostBenefitCalculator costBenefitCalculator, String outputDirectory) {
221-
return costBenefitCalculator.runCycleAnalysis();
222-
}
223-
224225
private void renderCycles(
225226
String outputDirectory,
226227
StringBuilder stringBuilder,
@@ -233,6 +234,16 @@ private void renderCycles(
233234
"<h2 align=\"center\">Class Cycles by the numbers: (Refactor starting with Priority 1)</h2>\n");
234235
stringBuilder.append("<table align=\"center\" border=\"5px\">\n");
235236

237+
String[] cycleTableHeadings;
238+
if (showDetails) {
239+
cycleTableHeadings = new String[] {
240+
"Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Minimum Cuts"
241+
};
242+
} else {
243+
cycleTableHeadings =
244+
new String[] {"Cycle Name", "Priority", "Class Count", "Relationship Count", "Minimum Cuts"};
245+
}
246+
236247
// Content
237248
stringBuilder.append("<thead>\n<tr>\n");
238249
for (String heading : cycleTableHeadings) {
@@ -250,16 +261,28 @@ private void renderCycles(
250261
edgesToCut.append("</br>\n");
251262
}
252263

253-
// "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min Cuts"
254-
String[] rankedCycleData = {
255-
rankedCycle.getCycleName(),
256-
rankedCycle.getPriority().toString(),
257-
rankedCycle.getChangePronenessRank().toString(),
258-
String.valueOf(rankedCycle.getCycleNodes().size()),
259-
String.valueOf(rankedCycle.getEdgeSet().size()),
260-
edgesToCut.toString()
261-
};
262-
264+
String[] rankedCycleData;
265+
if (showDetails) {
266+
rankedCycleData = new String[] {
267+
// "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min
268+
// Cuts"
269+
rankedCycle.getCycleName(),
270+
rankedCycle.getPriority().toString(),
271+
rankedCycle.getChangePronenessRank().toString(),
272+
String.valueOf(rankedCycle.getCycleNodes().size()),
273+
String.valueOf(rankedCycle.getEdgeSet().size()),
274+
edgesToCut.toString()
275+
};
276+
} else {
277+
rankedCycleData = new String[] {
278+
// "Cycle Name", "Priority", "Class Count", "Relationship Count", "Min Cuts"
279+
rankedCycle.getCycleName(),
280+
rankedCycle.getPriority().toString(),
281+
String.valueOf(rankedCycle.getCycleNodes().size()),
282+
String.valueOf(rankedCycle.getEdgeSet().size()),
283+
edgesToCut.toString()
284+
};
285+
}
263286
for (String rowData : rankedCycleData) {
264287
drawTableCell(rowData, stringBuilder);
265288
}

0 commit comments

Comments
 (0)