1717import org .hjug .cbc .RankedCycle ;
1818import org .hjug .cbc .RankedDisharmony ;
1919import org .hjug .git .GitLogReader ;
20+ import org .jgrapht .graph .DefaultEdge ;
2021
2122/**
2223 * Strictly HTML report that contains no JavaScript
@@ -82,6 +83,9 @@ public class SimpleHtmlReport {
8283 "Cycle Name" , "Priority" , "Change Proneness Rank" , "Class Count" , "Relationship Count" , "Minimum Cuts"
8384 };
8485
86+ // public final String[] classCycleTableHeadings = {"Classes", "Relationships", "Min Cut Edges"};
87+ public final String [] classCycleTableHeadings = {"Classes" , "Relationships" };
88+
8589 public void execute (
8690 boolean showDetails , String projectName , String projectVersion , String outputDirectory , File baseDir ) {
8791
@@ -156,7 +160,8 @@ public void execute(
156160 }
157161 List <RankedDisharmony > rankedGodClassDisharmonies = costBenefitCalculator .calculateGodClassCostBenefitValues ();
158162 List <RankedDisharmony > rankedCBODisharmonies = costBenefitCalculator .calculateCBOCostBenefitValues ();
159- List <RankedCycle > rankedCycles = costBenefitCalculator .runCycleAnalysis ();
163+
164+ List <RankedCycle > rankedCycles = runCycleAnalysis (costBenefitCalculator , outputDirectory );
160165
161166 if (rankedGodClassDisharmonies .isEmpty () && rankedCBODisharmonies .isEmpty ()) {
162167 stringBuilder
@@ -175,7 +180,15 @@ public void execute(
175180 if (!rankedGodClassDisharmonies .isEmpty () && !rankedCBODisharmonies .isEmpty ()) {
176181 stringBuilder .append ("<a href=\" #GOD\" >God Classes</a>" );
177182 stringBuilder .append ("<br/>" );
183+ }
184+
185+ if (!rankedCBODisharmonies .isEmpty ()) {
178186 stringBuilder .append ("<a href=\" #CBO\" >Highly Coupled Classes</a>" );
187+ stringBuilder .append ("<br/>" );
188+ }
189+
190+ if (!rankedCycles .isEmpty ()) {
191+ stringBuilder .append ("<a href=\" #CYCLES\" >Class Cycles</a>" );
179192 }
180193
181194 if (!rankedGodClassDisharmonies .isEmpty ()) {
@@ -197,6 +210,13 @@ public void execute(
197210 }
198211
199212 if (!rankedCycles .isEmpty ()) {
213+ if (!rankedGodClassDisharmonies .isEmpty () || !rankedCBODisharmonies .isEmpty ()) {
214+ stringBuilder .append ("<br/>" );
215+ stringBuilder .append ("<br/>" );
216+ stringBuilder .append ("<hr/>" );
217+ stringBuilder .append ("<br/>" );
218+ stringBuilder .append ("<br/>" );
219+ }
200220 renderCycles (outputDirectory , stringBuilder , rankedCycles , formatter );
201221 }
202222
@@ -210,13 +230,17 @@ public void execute(
210230 log .info ("Done! View the report at target/site/{}" , filename );
211231 }
212232
233+ public List <RankedCycle > runCycleAnalysis (CostBenefitCalculator costBenefitCalculator , String outputDirectory ) {
234+ return costBenefitCalculator .runCycleAnalysis (outputDirectory , false );
235+ }
236+
213237 private void renderCycles (
214238 String outputDirectory ,
215239 StringBuilder stringBuilder ,
216240 List <RankedCycle > rankedCycles ,
217241 DateTimeFormatter formatter ) {
218242
219- stringBuilder .append ("<div style=\" text-align: center;\" ><a id=\" CBO \" ><h1>Class Cycles</h1></a></div>" );
243+ stringBuilder .append ("<div style=\" text-align: center;\" ><a id=\" CYCLES \" ><h1>Class Cycles</h1></a></div>" );
220244
221245 stringBuilder .append (
222246 "<h2 align=\" center\" >Class Cycles by the numbers: (Refactor starting with Priority 1)</h2>" );
@@ -229,17 +253,23 @@ private void renderCycles(
229253 }
230254
231255 stringBuilder .append ("<tbody>" );
232- for (RankedCycle rankedCboClassDisharmony : rankedCycles ) {
256+ for (RankedCycle rankedCycle : rankedCycles ) {
233257 stringBuilder .append ("<tr>" );
234258
259+ StringBuilder edgesToCut = new StringBuilder ();
260+ for (DefaultEdge minCutEdge : rankedCycle .getMinCutEdges ()) {
261+ edgesToCut .append (minCutEdge .toString ());
262+ edgesToCut .append ("</br>" );
263+ }
264+
235265 // "Cycle Name", "Priority", "Change Proneness Rank", "Class Count", "Relationship Count", "Min Cuts"
236266 String [] rankedCycleData = {
237- rankedCboClassDisharmony .getCycleName (),
238- rankedCboClassDisharmony .getPriority ().toString (),
239- rankedCboClassDisharmony .getChangePronenessRank ().toString (),
240- String .valueOf (rankedCboClassDisharmony .getCycleNodes ().size ()),
241- String .valueOf (rankedCboClassDisharmony .getEdgeSet ().size ()),
242- rankedCboClassDisharmony . getMinCutEdges () .toString ()
267+ rankedCycle .getCycleName (),
268+ rankedCycle .getPriority ().toString (),
269+ rankedCycle .getChangePronenessRank ().toString (),
270+ String .valueOf (rankedCycle .getCycleNodes ().size ()),
271+ String .valueOf (rankedCycle .getEdgeSet ().size ()),
272+ edgesToCut .toString ()
243273 };
244274
245275 for (String rowData : rankedCycleData ) {
@@ -254,6 +284,70 @@ private void renderCycles(
254284 stringBuilder .append ("</tr></thead>" );
255285
256286 stringBuilder .append ("</table>" );
287+
288+ for (RankedCycle rankedCycle : rankedCycles ) {
289+ renderCycleTable (outputDirectory , stringBuilder , rankedCycle , formatter );
290+ }
291+ }
292+
293+ private void renderCycleTable (
294+ String outputDirectory , StringBuilder stringBuilder , RankedCycle cycle , DateTimeFormatter formatter ) {
295+
296+ stringBuilder .append ("<br/>" );
297+ stringBuilder .append ("<br/>" );
298+ stringBuilder .append ("<hr/>" );
299+ stringBuilder .append ("<br/>" );
300+ stringBuilder .append ("<br/>" );
301+
302+ stringBuilder .append ("<h2 align=\" center\" >Class Cycle : " + cycle .getCycleName () + "</h2>" );
303+ renderCycleImage (cycle .getCycleName (), stringBuilder , outputDirectory );
304+
305+ stringBuilder .append ("<div align=\" center\" >" );
306+ stringBuilder .append ("<strong>" );
307+ stringBuilder .append ("\" *\" indicates relationship(s) to remove to decompose cycle" );
308+ stringBuilder .append ("</strong>" );
309+ stringBuilder .append ("</div>" );
310+
311+ stringBuilder .append ("<table align=\" center\" border=\" 5px\" >" );
312+
313+ // Content
314+ stringBuilder .append ("<thead><tr>" );
315+ for (String heading : classCycleTableHeadings ) {
316+ stringBuilder .append ("<th>" ).append (heading ).append ("</th>" );
317+ }
318+
319+ stringBuilder .append ("<tbody>" );
320+
321+ for (String vertex : cycle .getVertexSet ()) {
322+ stringBuilder .append ("<tr>" );
323+ drawTableCell (vertex , stringBuilder );
324+ StringBuilder edges = new StringBuilder ();
325+ for (org .jgrapht .graph .DefaultEdge edge : cycle .getEdgeSet ()) {
326+ if (edge .toString ().startsWith ("(" + vertex + " :" )) {
327+ if (cycle .getMinCutEdges ().contains (edge )) {
328+ edges .append ("<strong>" );
329+ edges .append (edge + "*" );
330+ edges .append ("</strong>" );
331+ } else {
332+ edges .append (edge );
333+ }
334+
335+ edges .append ("<br/>" );
336+ }
337+ }
338+ drawTableCell (edges .toString (), stringBuilder );
339+ stringBuilder .append ("</tr>" );
340+ }
341+
342+ stringBuilder .append ("</tbody>" );
343+
344+ stringBuilder .append ("</tr></thead>" );
345+
346+ stringBuilder .append ("</table>" );
347+ }
348+
349+ public void renderCycleImage (String cycleName , StringBuilder stringBuilder , String outputDirectory ) {
350+ // empty on purpose
257351 }
258352
259353 private void renderGodClassInfo (
0 commit comments