Skip to content

Commit

Permalink
Merge pull request #107 from refactorfirst/render-dot-graphs-in-maven…
Browse files Browse the repository at this point in the history
…-report

Render dot graphs in maven report and log parsed sources
  • Loading branch information
jimbethancourt authored Oct 2, 2024
2 parents 691e632 + 91d5300 commit 3520766
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 50 deletions.
2 changes: 1 addition & 1 deletion change-proneness-ranker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.changepronenessranker</groupId>
Expand Down
13 changes: 12 additions & 1 deletion circular-reference-detector/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.circularreferencedetector</groupId>
Expand All @@ -13,6 +13,16 @@
<description>Tool to help detecting circular references by parsing a java project.</description>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
Expand All @@ -35,6 +45,7 @@
<version>5.9.0</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.jgrapht.graph.DefaultWeightedEdge;

@Slf4j
public class JavaProjectParser {

/**
Expand All @@ -37,6 +39,7 @@ public Graph<String, DefaultWeightedEdge> getClassReferences(String srcDirectory
filesStream
.filter(path -> path.getFileName().toString().endsWith(".java"))
.forEach(path -> {
log.info("Parsing {}", path);
List<String> types = getInstanceVarTypes(classNames, path.toFile());
types.addAll(getMethodArgumentTypes(classNames, path.toFile()));
if (!types.isEmpty()) {
Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<packaging>jar</packaging>
Expand Down
2 changes: 1 addition & 1 deletion cost-benefit-calculator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.costbenefitcalculator</groupId>
Expand Down
2 changes: 1 addition & 1 deletion coverage/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<artifactId>coverage</artifactId>
Expand Down
4 changes: 2 additions & 2 deletions effort-ranker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.effortranker</groupId>
Expand All @@ -20,7 +20,7 @@
<dependency>
<groupId>org.hjug.refactorfirst.testresources</groupId>
<artifactId>test-resources</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
4 changes: 2 additions & 2 deletions graph-data-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.graphdatagenerator</groupId>
Expand All @@ -15,7 +15,7 @@
<dependency>
<groupId>org.hjug.refactorfirst.costbenefitcalculator</groupId>
<artifactId>cost-benefit-calculator</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
<packaging>pom</packaging>

<url>https://github.com/refactorfirst/RefactorFirst</url>
Expand Down
2 changes: 1 addition & 1 deletion refactor-first-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.plugin</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ public String getDescription(Locale locale) {
+ " have the highest priority values.";
}

public final String[] cycleTableHeadings = {
"Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Minimum Cuts"
};

public final String[] classCycleTableHeadings = {"Classes", "Relationships"};

private Graph<String, DefaultWeightedEdge> classGraph;
Expand Down Expand Up @@ -164,6 +160,28 @@ public void executeReport(Locale locale) throws MavenReportException {

mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_START}, cboJavascript);
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_END}, null);

SinkEventAttributeSet d3js = new SinkEventAttributeSet();
d3js.addAttribute(SinkEventAttributes.TYPE, "text/javascript");
d3js.addAttribute(SinkEventAttributes.SRC, "https://d3js.org/d3.v5.min.js");

mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_START}, d3js);
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_END}, null);

SinkEventAttributeSet graphViz = new SinkEventAttributeSet();
graphViz.addAttribute(SinkEventAttributes.TYPE, "text/javascript");
graphViz.addAttribute(SinkEventAttributes.SRC, "https://unpkg.com/[email protected]/build/d3-graphviz.min.js");

mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_START}, graphViz);
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_END}, null);

SinkEventAttributeSet wasm = new SinkEventAttributeSet();
wasm.addAttribute(SinkEventAttributes.TYPE, "text/javascript");
wasm.addAttribute(SinkEventAttributes.SRC, "https://unpkg.com/@hpcc-js/[email protected]/dist/index.min.js");

mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_START}, wasm);
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_END}, null);

mainSink.head_();

mainSink.body();
Expand Down Expand Up @@ -472,12 +490,26 @@ private void renderCycles(
mainSink.section2_();
mainSink.division_();

mainSink.paragraph(alignCenter);
mainSink.text("Note: often only one minimum cut relationship needs to be removed");
mainSink.paragraph_();

mainSink.table();
mainSink.tableRows(new int[] {Sink.JUSTIFY_LEFT}, true);

// Content
// header row

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"};
}

mainSink.tableRow();
for (String heading : cycleTableHeadings) {
drawTableHeaderCell(heading, mainSink);
Expand All @@ -492,15 +524,27 @@ private void renderCycles(
edgesToCut.append(minCutEdge + ":" + (int) classGraph.getEdgeWeight(minCutEdge));
}

// "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min Cuts"
String[] rankedCycleData = {
rankedCycle.getCycleName(),
rankedCycle.getPriority().toString(),
rankedCycle.getChangePronenessRank().toString(),
String.valueOf(rankedCycle.getCycleNodes().size()),
String.valueOf(rankedCycle.getEdgeSet().size()),
edgesToCut.toString()
};
String[] rankedCycleData;
if (showDetails) {
// "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min Cuts"
rankedCycleData = new String[] {
rankedCycle.getCycleName(),
rankedCycle.getPriority().toString(),
rankedCycle.getChangePronenessRank().toString(),
String.valueOf(rankedCycle.getCycleNodes().size()),
String.valueOf(rankedCycle.getEdgeSet().size()),
edgesToCut.toString()
};
} else {
// "Cycle Name", "Priority", "Class Count", "Relationship Count", "Min Cuts"
rankedCycleData = new String[] {
rankedCycle.getCycleName(),
rankedCycle.getPriority().toString(),
String.valueOf(rankedCycle.getCycleNodes().size()),
String.valueOf(rankedCycle.getEdgeSet().size()),
edgesToCut.toString()
};
}

for (String rowData : rankedCycleData) {
drawCycleTableCell(rowData, mainSink);
Expand All @@ -513,12 +557,11 @@ private void renderCycles(
mainSink.table_();

for (RankedCycle rankedCycle : rankedCycles) {
renderCycleTable(outputDirectory, mainSink, rankedCycle, formatter);
renderCycle(outputDirectory, mainSink, rankedCycle, formatter);
}
}

private void renderCycleTable(
String outputDirectory, Sink mainSink, RankedCycle cycle, DateTimeFormatter formatter) {
private void renderCycle(String outputDirectory, Sink mainSink, RankedCycle cycle, DateTimeFormatter formatter) {

mainSink.lineBreak();
mainSink.lineBreak();
Expand All @@ -537,7 +580,7 @@ private void renderCycleTable(
mainSink.section2_();
mainSink.division_();

renderCycleImage(cycle.getCycleName(), mainSink, outputDirectory);
renderCycleImage(classGraph, cycle, mainSink);

mainSink.division(alignCenter);
mainSink.bold();
Expand Down Expand Up @@ -580,25 +623,6 @@ private void renderCycleTable(
mainSink.table_();
}

public void renderCycleImage(String cycleName, Sink mainSink, String outputDirectory) {
SinkEventAttributeSet alignCenter = new SinkEventAttributeSet();
alignCenter.addAttribute(SinkEventAttributes.ALIGN, "center");
mainSink.division(alignCenter);

SinkEventAttributeSet imageAttributes = new SinkEventAttributeSet();
imageAttributes.addAttribute(SinkEventAttributes.TYPE, "img");
imageAttributes.addAttribute(SinkEventAttributes.SRC, "./refactorFirst/cycles/graph" + cycleName + ".png");
imageAttributes.addAttribute(SinkEventAttributes.WIDTH, 1000);
imageAttributes.addAttribute(SinkEventAttributes.HEIGHT, 1000);
imageAttributes.addAttribute(SinkEventAttributes.ALT, "Cycle " + cycleName);

mainSink.unknown("img", new Object[] {HtmlMarkup.TAG_TYPE_SIMPLE}, imageAttributes);

mainSink.division_();
mainSink.lineBreak();
mainSink.lineBreak();
}

private void renderLegend(Sink mainSink, String legendHeading, String xAxis) {
SinkEventAttributeSet width = new SinkEventAttributeSet();
width.addAttribute(SinkEventAttributes.STYLE, "width:350px");
Expand Down Expand Up @@ -809,4 +833,90 @@ void writeGCBOGchartJs(List<RankedDisharmony> rankedDisharmonies, int maxPriorit
log.error("Error writing CBO chart script file", e);
}
}

void renderCycleImage(Graph<String, DefaultWeightedEdge> classGraph, RankedCycle cycle, Sink mainSink) {

SinkEventAttributeSet graphDivAttrs = new SinkEventAttributeSet();
graphDivAttrs.addAttribute(SinkEventAttributes.ALIGN, "center");
graphDivAttrs.addAttribute(SinkEventAttributes.ID, cycle.getCycleName());
graphDivAttrs.addAttribute(SinkEventAttributes.STYLE, "border: thin solid black");

mainSink.division(graphDivAttrs);
mainSink.division_();

String dot = buildDot(classGraph, cycle);

StringBuilder d3chart = new StringBuilder();
d3chart.append("d3.select(\"#" + cycle.getCycleName() + "\")\n");
d3chart.append(".graphviz()\n");
d3chart.append(".width(screen.width - 300)\n");
d3chart.append(".height(screen.height)\n");
d3chart.append(".fit(true)\n");
d3chart.append(".renderDot(" + dot + ");\n");

SinkEventAttributeSet dotChartScript = new SinkEventAttributeSet();
dotChartScript.addAttribute(SinkEventAttributes.TYPE, "text/javascript");

String script = "script";
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_START}, dotChartScript);

mainSink.rawText(d3chart.toString());
mainSink.unknown(script, new Object[] {HtmlMarkup.TAG_TYPE_END}, null);

SinkEventAttributeSet alignCenter = new SinkEventAttributeSet();
alignCenter.addAttribute(SinkEventAttributes.ALIGN, "center");

mainSink.paragraph(alignCenter);
mainSink.text("Red arrows represent relationship(s) to remove to decompose cycle");
mainSink.paragraph_();

mainSink.lineBreak();
mainSink.lineBreak();
}

String buildDot(Graph<String, DefaultWeightedEdge> classGraph, RankedCycle cycle) {
StringBuilder dot = new StringBuilder();

dot.append("'strict digraph G {\\n' +\n");

// render vertices
// e.g DownloadManager;
for (String vertex : cycle.getVertexSet()) {
dot.append("'");
dot.append(vertex);
dot.append(";\\n' +\n");
}

for (DefaultWeightedEdge edge : cycle.getEdgeSet()) {
// 'DownloadManager -> Download [ label="1" color="red" ];'

// render edge
String[] vertexes =
edge.toString().replace("(", "").replace(")", "").split(":");

String start = vertexes[0].trim();
String end = vertexes[1].trim();

dot.append("'");
dot.append(start);
dot.append(" -> ");
dot.append(end);

// render edge attributes
dot.append(" [ ");
dot.append("label = \"");
dot.append((int) classGraph.getEdgeWeight(edge));
dot.append("\"");

if (cycle.getMinCutEdges().contains(edge)) {
dot.append(" color = \"red\"");
}

dot.append(" ];\\n' +\n");
}

dot.append("'}'");

return dot.toString();
}
}
2 changes: 1 addition & 1 deletion report/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.hjug.refactorfirst</groupId>
<artifactId>refactor-first</artifactId>
<version>0.5.1-SNAPSHOT</version>
<version>0.6.0-SNAPSHOT</version>
</parent>

<groupId>org.hjug.refactorfirst.report</groupId>
Expand Down
Loading

0 comments on commit 3520766

Please sign in to comment.