11package org .hjug .refactorfirst .report ;
22
3- import java .util .HashSet ;
4- import java .util .List ;
5- import java .util .Locale ;
6- import java .util .Set ;
3+ import java .util .*;
74import lombok .extern .slf4j .Slf4j ;
85import org .hjug .cbc .RankedCycle ;
96import org .hjug .cbc .RankedDisharmony ;
107import org .hjug .gdg .GraphDataGenerator ;
8+ import org .hjug .graphbuilder .CodebaseGraphDTO ;
119import org .jgrapht .Graph ;
1210import org .jgrapht .graph .DefaultWeightedEdge ;
1311
@@ -417,6 +415,17 @@ public String printTitle(String projectName, String projectVersion) {
417415 return "<title>Refactor First Report for " + projectName + " " + projectVersion + " </title>\n " ;
418416 }
419417
418+ @ Override
419+ StringBuilder createMenu (
420+ List <DisharmonySpec > disharmonySpecs ,
421+ Map <String , List <RankedDisharmony >> rankedDisharmoniesByAnchor ,
422+ List <RankedCycle > rankedCycles ) {
423+ StringBuilder stringBuilder = new StringBuilder ();
424+ stringBuilder .append ("<li><a href=\" #CLASSMAP\" >Class Map</a></li>\n " );
425+ stringBuilder .append (super .createMenu (disharmonySpecs , rankedDisharmoniesByAnchor , rankedCycles ));
426+ return stringBuilder ;
427+ }
428+
420429 @ Override
421430 String renderGithubButtons () {
422431 return "<div align=\" center\" >\n " + "Show RefactorFirst some ❤️\n "
@@ -465,8 +474,8 @@ String renderDisharmonyChart(String anchorId, String title, List<RankedDisharmon
465474 }
466475
467476 @ Override
468- public String renderClassGraphVisuals () {
469- String dot = buildClassGraphDot (classGraph );
477+ public String renderClassGraphVisuals (String repoUrl , CodebaseGraphDTO codebaseGraphDTO ) {
478+ String dot = buildClassGraphDot (classGraph , repoUrl , codebaseGraphDTO );
470479 String classGraphName = "classGraph" ;
471480
472481 StringBuilder stringBuilder = new StringBuilder ();
@@ -491,7 +500,7 @@ public String renderClassGraphVisuals() {
491500
492501 private StringBuilder generateGraphButtons (String graphName , String dot ) {
493502 StringBuilder stringBuilder = new StringBuilder ();
494- stringBuilder .append ("<h1 align=\" center\" >Class Map</h1>" );
503+ stringBuilder .append ("<h1 align=\" center\" ><a id= \" CLASSMAP \" > Class Map</a> </h1>" );
495504 stringBuilder .append ("<script>\n " );
496505 stringBuilder .append ("const " + graphName + "_dot = " + dot + "\n " );
497506 stringBuilder .append ("</script>\n " );
@@ -501,7 +510,9 @@ private StringBuilder generateGraphButtons(String graphName, String dot) {
501510
502511 stringBuilder .append ("<div align=\" center\" >\n Red lines represent relationships to remove.<br>\n " );
503512 stringBuilder .append ("Red nodes represent classes to remove.<br>\n " );
504- stringBuilder .append ("Zoom in / out with your mouse wheel and click/move to drag the image.\n " );
513+ stringBuilder .append ("Zoom in / out with your mouse wheel and click/move to drag the image.<br>\n " );
514+ stringBuilder .append (
515+ "Clicking on a node in the DOT graph (if present below) will open its source file in the repo. It will not open a new browser window.\n " );
505516 stringBuilder .append ("</div>\n " );
506517 return stringBuilder ;
507518 }
@@ -511,7 +522,7 @@ private static String generateDotImage(String graphName) {
511522 return "<div id=\" " + graphName
512523 + "\" style=\" width: 95%; height: 70vh; margin: auto; border: thin solid black\" ></div>\n "
513524 + "<script type=\" module\" >\n "
514- + "import init, { DotParser } from \" https://esm.run/ @vizdom/vizdom-ts-web\" ;\n "
525+ + "import init, { DotParser } from \" https://cdn.jsdelivr.net/npm/ @vizdom/vizdom-ts-web@0.1.19/vizdom_ts.min.js \" ;\n "
515526 + " if(DotParser) {\n "
516527 + " // Wait for the WASM binary to be compiled and the 'wasm' object to be populated\n "
517528 + " await init();\n "
@@ -534,7 +545,8 @@ private static String generateDotImage(String graphName) {
534545 + " }\n " + "</script>\n " ;
535546 }
536547
537- String buildClassGraphDot (Graph <String , DefaultWeightedEdge > classGraph ) {
548+ String buildClassGraphDot (
549+ Graph <String , DefaultWeightedEdge > classGraph , String repoUrl , CodebaseGraphDTO codebaseGraphDTO ) {
538550 StringBuilder dot = new StringBuilder ();
539551 dot .append ("`strict digraph G {\n " );
540552
@@ -563,17 +575,26 @@ String buildClassGraphDot(Graph<String, DefaultWeightedEdge> classGraph) {
563575
564576 dot .append (className .replace ("$" , "_" ));
565577
578+ dot .append (" [" );
579+ dot .append (hyperlinkClassForDot (vertex , repoUrl , codebaseGraphDTO ));
580+
566581 if (vertexesToRemove .contains (vertex )) {
567- dot .append (" [ color=red style=filled] \n " );
582+ dot .append (" color=red style=filled" );
568583 }
569584
570- dot .append (";\n " );
585+ dot .append ("] ;\n " );
571586 }
572587
573588 dot .append ("}`;" );
574589 return dot .toString ();
575590 }
576591
592+ String hyperlinkClassForDot (String fqClassName , String repoUrl , CodebaseGraphDTO codebaseGraphDTO ) {
593+ StringBuilder sb = new StringBuilder ();
594+ String path = codebaseGraphDTO .getClassToSourceFilePathMapping ().get (fqClassName );
595+ return sb .append ("URL=\" " + repoUrl + path + "\" target=\" _blank\" " ).toString ();
596+ }
597+
577598 private void renderEdge (
578599 Graph <String , DefaultWeightedEdge > classGraph , DefaultWeightedEdge edge , StringBuilder dot ) {
579600 // render edge
@@ -622,8 +643,8 @@ private void renderEdge(
622643 }
623644
624645 @ Override
625- public String renderCycleVisuals (RankedCycle cycle ) {
626- String dot = buildCycleDot (classGraph , cycle );
646+ public String renderCycleVisuals (RankedCycle cycle , String repoUrl , CodebaseGraphDTO codebaseGraphDTO ) {
647+ String dot = buildCycleDot (classGraph , cycle , repoUrl , codebaseGraphDTO );
627648
628649 String cycleName = getClassName (cycle .getCycleName ()).replace ("$" , "_" );
629650
@@ -643,7 +664,11 @@ public String renderCycleVisuals(RankedCycle cycle) {
643664 return stringBuilder .toString ();
644665 }
645666
646- String buildCycleDot (Graph <String , DefaultWeightedEdge > classGraph , RankedCycle cycle ) {
667+ String buildCycleDot (
668+ Graph <String , DefaultWeightedEdge > classGraph ,
669+ RankedCycle cycle ,
670+ String repoUrl ,
671+ CodebaseGraphDTO codebaseGraphDTO ) {
647672 StringBuilder dot = new StringBuilder ();
648673 dot .append ("`strict digraph G {\n " );
649674
@@ -653,18 +678,29 @@ String buildCycleDot(Graph<String, DefaultWeightedEdge> classGraph, RankedCycle
653678
654679 // render vertices
655680 for (String vertex : cycle .getVertexSet ()) {
656- dot .append (getClassName (vertex ).replace ("$" , "_" ));
681+ String className = getClassName (vertex );
682+
683+ // if the vertex is a nested class and has no outgoing edges, skip it
684+ if (className .contains ("$" )
685+ && className .split ("\\ $" )[className .split ("\\ $" ).length - 1 ].matches ("\\ d+" )
686+ && classGraph .outDegreeOf (vertex ) == 0 ) {
687+ continue ;
688+ }
689+
690+ dot .append (className .replace ("$" , "_" ));
691+
692+ dot .append (" [" );
693+ dot .append (hyperlinkClassForDot (vertex , repoUrl , codebaseGraphDTO ));
657694
658695 if (vertexesToRemove .contains (vertex )) {
659- dot .append (" [ color=red style=filled] \n " );
696+ dot .append (" color=red style=filled" );
660697 }
661698
662- dot .append (";\n " );
699+ dot .append ("] ;\n " );
663700 }
664701
665702 dot .append ("}`;" );
666-
667- return dot .toString ().replace ("$" , "_" );
703+ return dot .toString ();
668704 }
669705
670706 String generate2DPopup (String cycleName ) {
0 commit comments