extends JavaIsoVisitor
implements TypeProcessor {
+
+ private final JavaMethodInvocationVisitor methodInvocationVisitor;
+ private final JavaNewClassVisitor newClassVisitor;
+
+ @Getter
+ private Graph
+ */
+@Getter
+public class JavaFqnCapturingVisitor extends JavaIsoVisitor {
+
+ // consider using ConcurrentHashMap to scale performance
+ // package -> name, FQN
+ private final Map extends JavaIsoVisitor implements TypeProcessor {
+
+ @Getter
+ private Graph extends JavaIsoVisitor implements TypeProcessor {
+
+ @Getter
+ private Graph extends JavaIsoVisitor implements TypeProcessor {
+
+ // used to keep track of what packages are in the codebase
+ // used to remove the nodes that are not in the codebase
+ @Getter
+ private final Set javaClassDeclarationVisitor;
+
+ public JavaVisitor(
+ Graph Red arrows represent relationship(s) to remove to decompose cycle Note: often only one minimum cut relationship needs to be removed Bold edges are backward edges causing
+ // cyclesGod Class Chart Legend:
" + " \n"
+ " \n"
+ "
"
+ " \n"
@@ -23,7 +371,7 @@ public class HtmlReport extends SimpleHtmlReport {
+ " X-Axis: Effort to refactor to a non-God class
";
- public static final String COUPLING_BETWEEN_OBJECT_CHART_LEGEND =
+ private static final String COUPLING_BETWEEN_OBJECT_CHART_LEGEND =
" Coupling Between Objects Chart Legend:
" + " \n"
+ " \n"
+ "
\n");
+
+ return stringBuilder.toString();
}
- public void renderCycleImage(
- Graph \n"
@@ -35,48 +383,57 @@ public class HtmlReport extends SimpleHtmlReport {
+ " X-Axis: Number of objects the class is coupled to
";
@Override
- public void printHead(StringBuilder stringBuilder) {
- stringBuilder.append(""
- + "\n"
- + "\n"
+ public String printHead() {
+ // !Remember to update RefactorFirstMavenReport if this is modified
+ return // GH Buttons import
+ "\n"
+ // google chart import
+ + "\n"
+ // d3 dot graph imports
+ "\n"
- + "\n"
+ + "\n"
+ "\n"
- + " \n");
+ // sigma graph imports - sigma, graphology, graphlib, and graphlib-dot
+ + "\n"
+ + "\n"
+ // may only need graphlib-dot
+ + "\n"
+ + "\n"
+ + "\n";
+ }
+
+ String printScripts() {
+ return SUGIYAMA_SIGMA_GRAPH + FORCE_3D_GRAPH + POPUP_FUNCTIONS + POPUP_STYLE;
}
@Override
- public void printTitle(String projectName, String projectVersion, StringBuilder stringBuilder) {
- stringBuilder
- .append("
\n");
- stringBuilder.append(
- "Star\n");
- stringBuilder.append(
- "Fork\n");
- stringBuilder.append(
- "Watch\n");
- stringBuilder.append(
- "Issue\n");
- stringBuilder.append(
- "Sponsor\n");
- stringBuilder.append("
\n"
+ + "Star\n"
+ + "Fork\n"
+ + "Watch\n"
+ + "Issue\n"
+ + "Sponsor\n"
+ + "Class Map
");
+ stringBuilder.append(
+ "
\n");
+ stringBuilder.append("Zoom in / out with your mouse wheel and click/move to drag the image.\n");
+ stringBuilder.append("
");
- stringBuilder.append("
");
+ // revisit and add D3 popup button as well
+ if (classGraph.vertexSet().size() + classGraph.edgeSet().size() < d3Threshold) {
+ stringBuilder.append(
+ "\n");
+ stringBuilder.append("\n");
+ } else {
+ // revisit and add D3 SVG popup button
+ stringBuilder.append("
\n");
+ stringBuilder.append("Zoom in / out with your mouse wheel and click/move to drag the image.\n");
+ stringBuilder.append("
\n");
+ stringBuilder.append("
\n");
+
+ return stringBuilder.toString();
+ }
+
+ String buildCycleDot(Graph
\n");
+ }
+
+ if (!rankedGodClassDisharmonies.isEmpty()) {
stringBuilder.append("God Classes\n");
stringBuilder.append("
\n");
}
@@ -183,110 +278,146 @@ public void execute(
stringBuilder.append("Class Cycles\n");
}
- if (!rankedGodClassDisharmonies.isEmpty()) {
- renderGodClassInfo(
- showDetails,
- outputDirectory,
- rankedGodClassDisharmonies,
- stringBuilder,
- godClassTableHeadings,
- formatter);
+ log.info("Generating HTML Report");
+
+ stringBuilder.append(renderClassGraphDotImage());
+ stringBuilder.append("
\n");
+ stringBuilder.append(renderGithubButtons());
+
+ // Display impact of each edge if removed
+ stringBuilder.append("
\n");
+ String edgeInfos = renderEdgeToRemoveInfos(edgeToRemoveInfos);
+
+ if (!edgeToRemoveInfos.isEmpty()) {
+ stringBuilder.append(edgeInfos);
+ stringBuilder.append(renderGithubButtons());
+ stringBuilder.append("
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n");
}
- if (!rankedGodClassDisharmonies.isEmpty() && !rankedCBODisharmonies.isEmpty()) {
+ if (!rankedGodClassDisharmonies.isEmpty()) {
+ final String[] godClassTableHeadings =
+ showDetails ? godClassDetailedTableHeadings : godClassSimpleTableHeadings;
+ stringBuilder.append(renderGodClassInfo(showDetails, rankedGodClassDisharmonies, godClassTableHeadings));
stringBuilder.append("
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n");
}
if (!rankedCBODisharmonies.isEmpty()) {
- renderHighlyCoupledClassInfo(outputDirectory, stringBuilder, rankedCBODisharmonies, formatter);
+ stringBuilder.append(renderHighlyCoupledClassInfo(rankedCBODisharmonies));
+ stringBuilder.append("
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n");
}
if (!rankedCycles.isEmpty()) {
- if (!rankedGodClassDisharmonies.isEmpty() || !rankedCBODisharmonies.isEmpty()) {
- stringBuilder.append("
\n");
- stringBuilder.append("
\n");
- stringBuilder.append("
\n");
- stringBuilder.append("
\n");
- stringBuilder.append("
\n");
- }
- renderCycles(outputDirectory, stringBuilder, rankedCycles, formatter);
+ stringBuilder.append(renderCycles(rankedCycles));
}
stringBuilder.append("\n");
- printProjectFooter(stringBuilder, formatter);
- stringBuilder.append(THE_END);
log.debug(stringBuilder.toString());
-
- writeReportToDisk(outputDirectory, filename, stringBuilder);
- log.info("Done! View the report at target/site/{}", filename);
+ return stringBuilder;
}
- private void renderCycles(
- String outputDirectory,
- StringBuilder stringBuilder,
- List
\n");
+
+ rankedCycles.stream().limit(10).map(this::renderSingleCycle).forEach(stringBuilder::append);
+
+ return stringBuilder.toString();
+ }
+
+ private String renderEdgeToRemoveInfos(ListClass Cycles by the numbers: (Refactor starting with Priority 1)
\n");
- stringBuilder.append(
- "
\n");
+ stringBuilder
+ .append("Current Average Cycle Node Count: ")
+ .append(dsm.getAverageCycleNodeCount())
+ .append("
\n");
+ stringBuilder
+ .append("Current Total Back Edge Count: ")
+ .append(dsm.getEdgesAboveDiagonal().size())
+ .append("
\n");
+ stringBuilder
+ .append("Current Total Min Weight Back Edge Count: ")
+ .append(dsm.getMinimumWeightEdgesAboveDiagonal().size())
+ .append("
\n");
+ stringBuilder.append("\n");
- String[] cycleTableHeadings;
- if (showDetails) {
- cycleTableHeadings = new String[] {
- "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Minimum Cuts"
- };
- } else {
- cycleTableHeadings =
- new String[] {"Cycle Name", "Priority", "Class Count", "Relationship Count", "Minimum Cuts"};
+ // Content
+ stringBuilder.append("\n
\n");
+
+ return stringBuilder.toString();
+ }
+
+ private String renderClassCycleSummary(List\n");
+ for (String heading : getEdgesToRemoveInfoTableHeadings()) {
+ stringBuilder.append(" \n");
+
+ stringBuilder.append("\n");
+ for (EdgeToRemoveInfo edge : edges) {
+ stringBuilder.append("").append(heading).append(" \n");
+ }
+ stringBuilder.append("\n");
+
+ for (String rowData : getEdgeToRemoveInfos(edge)) {
+ stringBuilder.append(drawTableCell(rowData));
+ }
+
+ stringBuilder.append(" \n");
+ }
+
+ stringBuilder.append("\n");
+ stringBuilder.append("Class Cycles by the numbers:
\n");
+ // stringBuilder.append("\n");
+
// Content
stringBuilder.append("\n
\n");
- for (RankedCycle rankedCycle : rankedCycles) {
- renderSingleCycle(outputDirectory, stringBuilder, rankedCycle, formatter);
- }
+ return stringBuilder.toString();
}
- private void renderSingleCycle(
- String outputDirectory, StringBuilder stringBuilder, RankedCycle cycle, DateTimeFormatter formatter) {
+ private String renderEdge(DefaultWeightedEdge edge) {
+ StringBuilder edgesToCut = new StringBuilder();
+ String[] vertexes = extractVertexes(edge);
+ String start = getClassName(vertexes[0].trim());
+ String end = getClassName(vertexes[1].trim());
+
+ // → is HTML "Right Arrow" code
+ return edgesToCut
+ .append(start + " → " + end + " : " + (int) classGraph.getEdgeWeight(edge))
+ .toString();
+ }
+
+ private String[] getCycleSummaryTableHeadings() {
+ return new String[] {"Cycle Name", "Priority", "Class Count", "Relationship Count" /*, "Minimum Cuts"*/};
+ }
+
+ private String[] getEdgesToRemoveInfoTableHeadings() {
+ return new String[] {
+ "Edge",
+ "Edge Weight",
+ "In # of Cycles",
+ "New Cycle Count",
+ "New Avg Cycle Node Count",
+ "Avg Node Δ ÷ Effort"
+ };
+ }
+
+ private String[] getEdgeToRemoveInfos(EdgeToRemoveInfo edgeToRemoveInfo) {
+ return new String[] {
+ // "Edge", "Edge Weight", "In # of Cycles", "New Cycle Count", "New Avg Cycle Node Count", "Avg Node Count /
+ // Effort"
+ renderEdge(edgeToRemoveInfo.getEdge()),
+ String.valueOf((int) edgeToRemoveInfo.getEdgeWeight()),
+ String.valueOf(edgeToRemoveInfo.getEdgeInCycleCount()),
+ String.valueOf(edgeToRemoveInfo.getNewCycleCount()),
+ String.valueOf(edgeToRemoveInfo.getAverageCycleNodeCount()),
+ String.valueOf(edgeToRemoveInfo.getPayoff())
+ };
+ }
+
+ private String[] getRankedCycleSummaryData(RankedCycle rankedCycle, StringBuilder edgesToCut) {
+ return new String[] {
+ // "Cycle Name", "Priority", "Class Count", "Relationship Count", "Min Cuts"
+ getClassName(rankedCycle.getCycleName()),
+ rankedCycle.getPriority().toString(),
+ String.valueOf(rankedCycle.getCycleNodes().size()),
+ String.valueOf(rankedCycle.getEdgeSet().size()) // ,
+ // edgesToCut.toString()
+ };
+ }
+
+ private String renderSingleCycle(RankedCycle cycle) {
+ StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("\n");
- for (String heading : cycleTableHeadings) {
+ for (String heading : getCycleSummaryTableHeadings()) {
stringBuilder.append(" \n");
stringBuilder.append("\n");
- for (RankedCycle rankedCycle : rankedCycles) {
+ for (RankedCycle cycle : rankedCycles) {
stringBuilder.append("").append(heading).append(" \n");
}
stringBuilder.append("\n");
- StringBuilder edgesToCut = new StringBuilder();
- for (DefaultWeightedEdge minCutEdge : rankedCycle.getMinCutEdges()) {
- edgesToCut.append(minCutEdge + ":" + (int) classGraph.getEdgeWeight(minCutEdge));
- edgesToCut.append("\n");
+ StringBuilder edges = new StringBuilder();
+ for (DefaultWeightedEdge edge : cycle.getMinCutEdges()) {
+
+ if (edgesAboveDiagonal.contains(edge)) {
+ stringBuilder.append("");
+ edges.append(renderEdge(edge));
+ stringBuilder.append("");
+ } else {
+ edges.append(renderEdge(edge));
+ }
+ edges.append("\n");
}
- String[] rankedCycleData;
- if (showDetails) {
- rankedCycleData = new String[] {
- // "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min
- // Cuts"
- rankedCycle.getCycleName(),
- rankedCycle.getPriority().toString(),
- rankedCycle.getChangePronenessRank().toString(),
- String.valueOf(rankedCycle.getCycleNodes().size()),
- String.valueOf(rankedCycle.getEdgeSet().size()),
- edgesToCut.toString()
- };
- } else {
- rankedCycleData = new String[] {
- // "Cycle Name", "Priority", "Class Count", "Relationship Count", "Min Cuts"
- rankedCycle.getCycleName(),
- rankedCycle.getPriority().toString(),
- String.valueOf(rankedCycle.getCycleNodes().size()),
- String.valueOf(rankedCycle.getEdgeSet().size()),
- edgesToCut.toString()
- };
- }
- for (String rowData : rankedCycleData) {
- drawTableCell(rowData, stringBuilder);
+ for (String rowData : getRankedCycleSummaryData(cycle, edges)) {
+ stringBuilder.append(drawTableCell(rowData));
}
stringBuilder.append(" \n");
@@ -295,13 +426,62 @@ private void renderCycles(
stringBuilder.append("\n");
stringBuilder.append("
\n");
stringBuilder.append("
\n");
@@ -309,13 +489,13 @@ private void renderSingleCycle(
stringBuilder.append("
\n");
stringBuilder.append("
\n");
- stringBuilder.append("Class Cycle : " + cycle.getCycleName() + "
\n");
- // renderCycleImage(cycle.getCycleName(), stringBuilder, outputDirectory);
- renderCycleImage(classGraph, cycle, stringBuilder);
+ stringBuilder.append("Class Cycle : " + getClassName(cycle.getCycleName()) + "
\n");
+ stringBuilder.append(renderCycleDotImage(cycle));
stringBuilder.append("
\"*\" indicates that edge is also a minimum cut edge in the cycle");
stringBuilder.append("");
stringBuilder.append("");
- drawTableCell(vertex, stringBuilder);
+ stringBuilder.append(drawTableCell(getClassName(vertex)));
StringBuilder edges = new StringBuilder();
- for (org.jgrapht.graph.DefaultWeightedEdge edge : cycle.getEdgeSet()) {
+ for (DefaultWeightedEdge edge : cycle.getEdgeSet()) {
if (edge.toString().startsWith("(" + vertex + " :")) {
- if (cycle.getMinCutEdges().contains(edge)) {
+
+ if (edgesAboveDiagonal.contains(edge)) {
edges.append("");
- edges.append(edge);
- edges.append(":")
- .append((int) classGraph.getEdgeWeight(edge))
- .append("*");
+ edges.append(renderEdge(edge));
+ if (cycle.getMinCutEdges().contains(edge)) {
+ edges.append("*");
+ }
edges.append("");
} else {
- edges.append(edge);
- edges.append(":").append((int) classGraph.getEdgeWeight(edge));
+ edges.append(renderEdge(edge));
}
edges.append(" \n");
}
stringBuilder.append("
\n");
}
}
- drawTableCell(edges.toString(), stringBuilder);
+ stringBuilder.append(drawTableCell(edges.toString()));
stringBuilder.append("God classes by the numbers: (Refactor Starting with Priority 1)
\n");
@@ -427,7 +608,7 @@ private void renderGodClassInfo(
showDetails ? detailedRankedGodClassDisharmonyData : simpleRankedGodClassDisharmonyData;
for (String rowData : rankedDisharmonyData) {
- drawTableCell(rowData, stringBuilder);
+ stringBuilder.append(drawTableCell(rowData));
}
stringBuilder.append("\n");
@@ -435,20 +616,19 @@ private void renderGodClassInfo(
stringBuilder.append("\n");
stringBuilder.append("\n");
+
+ return stringBuilder.toString();
}
- private void renderHighlyCoupledClassInfo(
- String outputDirectory,
- StringBuilder stringBuilder,
- ListHighly Coupled classes by the numbers: (Refactor starting with Priority 1)
");
@@ -475,7 +655,7 @@ private void renderHighlyCoupledClassInfo(
};
for (String rowData : rankedCboClassDisharmonyData) {
- drawTableCell(rowData, stringBuilder);
+ stringBuilder.append(drawTableCell(rowData));
}
stringBuilder.append("");
@@ -483,13 +663,23 @@ private void renderHighlyCoupledClassInfo(
stringBuilder.append("");
stringBuilder.append("");
+
+ return stringBuilder.toString();
}
- void drawTableCell(String rowData, StringBuilder stringBuilder) {
+ String drawTableCell(String rowData) {
if (isNumber(rowData) || isDateTime(rowData)) {
- stringBuilder.append("").append(rowData).append(" \n");
+ return new StringBuilder()
+ .append("")
+ .append(rowData)
+ .append(" \n")
+ .toString();
} else {
- stringBuilder.append("").append(rowData).append(" \n");
+ return new StringBuilder()
+ .append("")
+ .append(rowData)
+ .append(" \n")
+ .toString();
}
}
@@ -501,50 +691,52 @@ boolean isDateTime(String rowData) {
return rowData.contains(", ");
}
- public void printTitle(String projectName, String projectVersion, StringBuilder stringBuilder) {
- // empty on purpose
+ public String printTitle(String projectName, String projectVersion) {
+ return ""; // empty on purpose
+ }
+
+ public String printHead() {
+ return ""; // empty on purpose
+ }
+
+ String printScripts() {
+ return ""; // empty on purpose
}
- public void printHead(StringBuilder stringBuilder) {
- // empty on purpose
+ public String printOpenBodyTag() {
+ return " \n";
}
- public void printBreadcrumbs(StringBuilder stringBuilder) {
- stringBuilder.append(" \n"
- + "