From cb5f997b21df9677ec96a228632ab0852e727a3e Mon Sep 17 00:00:00 2001 From: Jim Bethancourt Date: Fri, 20 Sep 2024 12:37:18 -0500 Subject: [PATCH 1/2] First attempt at capturing edge weights --- .../app/CircularReferenceDetectorApp.java | 16 +++++----- .../CircularReferenceChecker.java | 15 +++++----- .../org/hjug/parser/JavaProjectParser.java | 29 +++++++++++++++---- .../CircularReferenceCheckerTests.java | 8 ++--- .../hjug/parser/JavaProjectParserTests.java | 6 ++-- .../com/ideacrest/parser/testclasses/E.java | 1 + .../org/hjug/cbc/CostBenefitCalculator.java | 25 +++++++++++----- .../main/java/org/hjug/cbc/RankedCycle.java | 10 +++---- .../report/SimpleHtmlReport.java | 20 +++++++++---- 9 files changed, 86 insertions(+), 44 deletions(-) diff --git a/circular-reference-detector/src/main/java/org/hjug/app/CircularReferenceDetectorApp.java b/circular-reference-detector/src/main/java/org/hjug/app/CircularReferenceDetectorApp.java index b5b5db6f..0ac79d40 100644 --- a/circular-reference-detector/src/main/java/org/hjug/app/CircularReferenceDetectorApp.java +++ b/circular-reference-detector/src/main/java/org/hjug/app/CircularReferenceDetectorApp.java @@ -10,7 +10,7 @@ import org.jgrapht.alg.flow.GusfieldGomoryHuCutTree; import org.jgrapht.graph.AsSubgraph; import org.jgrapht.graph.AsUndirectedGraph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; /** * Command line application to detect circular references in a java project. @@ -41,7 +41,7 @@ public void launchApp(String[] args) { String outputDirectoryPath = args[1]; JavaProjectParser javaProjectParser = new JavaProjectParser(); try { - Graph classReferencesGraph = + Graph classReferencesGraph = javaProjectParser.getClassReferences(srcDirectoryPath); detectAndStoreCyclesInDirectory(outputDirectoryPath, classReferencesGraph); } catch (Exception e) { @@ -51,9 +51,9 @@ public void launchApp(String[] args) { } private void detectAndStoreCyclesInDirectory( - String outputDirectoryPath, Graph classReferencesGraph) { + String outputDirectoryPath, Graph classReferencesGraph) { CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker(); - Map> cyclesForEveryVertexMap = + Map> cyclesForEveryVertexMap = circularReferenceChecker.detectCycles(classReferencesGraph); cyclesForEveryVertexMap.forEach((vertex, subGraph) -> { try { @@ -64,13 +64,13 @@ private void detectAndStoreCyclesInDirectory( renderedSubGraphs.put(vertex, subGraph); System.out.println( "Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount); - GusfieldGomoryHuCutTree gusfieldGomoryHuCutTree = + GusfieldGomoryHuCutTree gusfieldGomoryHuCutTree = new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph)); double minCut = gusfieldGomoryHuCutTree.calculateMinCut(); System.out.println("Min cut weight: " + minCut); - Set minCutEdges = gusfieldGomoryHuCutTree.getCutEdges(); + Set minCutEdges = gusfieldGomoryHuCutTree.getCutEdges(); System.out.println("Minimum Cut Edges:"); - for (DefaultEdge minCutEdge : minCutEdges) { + for (DefaultWeightedEdge minCutEdge : minCutEdges) { System.out.println(minCutEdge); } } @@ -80,7 +80,7 @@ private void detectAndStoreCyclesInDirectory( }); } - private boolean isDuplicateSubGraph(AsSubgraph subGraph, String vertex) { + private boolean isDuplicateSubGraph(AsSubgraph subGraph, String vertex) { if (!renderedSubGraphs.isEmpty()) { for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) { if (renderedSubGraph.vertexSet().size() == subGraph.vertexSet().size() diff --git a/circular-reference-detector/src/main/java/org/hjug/cycledetector/CircularReferenceChecker.java b/circular-reference-detector/src/main/java/org/hjug/cycledetector/CircularReferenceChecker.java index 2cbe18ed..c1302e2b 100644 --- a/circular-reference-detector/src/main/java/org/hjug/cycledetector/CircularReferenceChecker.java +++ b/circular-reference-detector/src/main/java/org/hjug/cycledetector/CircularReferenceChecker.java @@ -14,7 +14,7 @@ import org.jgrapht.alg.cycle.CycleDetector; import org.jgrapht.ext.JGraphXAdapter; import org.jgrapht.graph.AsSubgraph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; public class CircularReferenceChecker { @@ -25,11 +25,12 @@ public class CircularReferenceChecker { * @param classReferencesGraph * @return a Map of Class and its Cycle Graph */ - public Map> detectCycles(Graph classReferencesGraph) { - Map> cyclesForEveryVertexMap = new HashMap<>(); - CycleDetector cycleDetector = new CycleDetector<>(classReferencesGraph); + public Map> detectCycles( + Graph classReferencesGraph) { + Map> cyclesForEveryVertexMap = new HashMap<>(); + CycleDetector cycleDetector = new CycleDetector<>(classReferencesGraph); cycleDetector.findCycles().forEach(v -> { - AsSubgraph subGraph = + AsSubgraph subGraph = new AsSubgraph<>(classReferencesGraph, cycleDetector.findCyclesContainingVertex(v)); cyclesForEveryVertexMap.put(v, subGraph); }); @@ -45,12 +46,12 @@ public Map> detectCycles(Graph subGraph, String imageName) + public void createImage(String outputDirectoryPath, Graph subGraph, String imageName) throws IOException { new File(outputDirectoryPath).mkdirs(); File imgFile = new File(outputDirectoryPath + "/graph" + imageName + ".png"); if (imgFile.createNewFile()) { - JGraphXAdapter graphAdapter = new JGraphXAdapter<>(subGraph); + JGraphXAdapter graphAdapter = new JGraphXAdapter<>(subGraph); mxIGraphLayout layout = new mxCircleLayout(graphAdapter); layout.execute(graphAdapter.getDefaultParent()); diff --git a/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java b/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java index b55f40f7..135fef3c 100644 --- a/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java +++ b/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java @@ -15,8 +15,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.jgrapht.Graph; -import org.jgrapht.graph.DefaultDirectedGraph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.builder.GraphTypeBuilder; public class JavaProjectParser { @@ -26,8 +26,14 @@ public class JavaProjectParser { * @return * @throws IOException */ - public Graph getClassReferences(String srcDirectory) throws IOException { - Graph classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class); + public Graph getClassReferences(String srcDirectory) throws IOException { + Graph classReferencesGraph = + GraphTypeBuilder.directed() + .allowingMultipleEdges(false) + .allowingSelfLoops(true) + .edgeClass(DefaultWeightedEdge.class) + .weighted(true) + .buildGraph(); if (srcDirectory == null || srcDirectory.isEmpty()) { throw new IllegalArgumentException(); } else { @@ -43,13 +49,26 @@ public Graph getClassReferences(String srcDirectory) throws getClassName(path.getFileName().toString()); classReferencesGraph.addVertex(className); varTypes.forEach(classReferencesGraph::addVertex); - varTypes.forEach(var -> classReferencesGraph.addEdge(className, var)); + varTypes.forEach(varType -> { + DefaultWeightedEdge weightedEdge = classReferencesGraph.addEdge(className, varType); + + // not sure why some edges are null, but let's ignore them for now + if (null != weightedEdge) { + classReferencesGraph.setEdgeWeight( + weightedEdge, classReferencesGraph.getEdgeWeight(weightedEdge) + 1); + } + }); } }); } catch (FileNotFoundException e) { e.printStackTrace(); } } + + for (DefaultWeightedEdge weightedEdge : classReferencesGraph.edgeSet()) { + System.out.println(weightedEdge.toString() + ":" + classReferencesGraph.getEdgeWeight(weightedEdge)); + } + return classReferencesGraph; } diff --git a/circular-reference-detector/src/test/java/org/hjug/cycledetector/CircularReferenceCheckerTests.java b/circular-reference-detector/src/test/java/org/hjug/cycledetector/CircularReferenceCheckerTests.java index 2a64f74d..26bbd540 100644 --- a/circular-reference-detector/src/test/java/org/hjug/cycledetector/CircularReferenceCheckerTests.java +++ b/circular-reference-detector/src/test/java/org/hjug/cycledetector/CircularReferenceCheckerTests.java @@ -9,7 +9,7 @@ import org.jgrapht.Graph; import org.jgrapht.graph.AsSubgraph; import org.jgrapht.graph.DefaultDirectedGraph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -20,14 +20,14 @@ class CircularReferenceCheckerTests { @DisplayName("Detect 3 cycles from given graph.") @Test public void detectCyclesTest() { - Graph classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class); + Graph classReferencesGraph = new DefaultDirectedGraph<>(DefaultWeightedEdge.class); classReferencesGraph.addVertex("A"); classReferencesGraph.addVertex("B"); classReferencesGraph.addVertex("C"); classReferencesGraph.addEdge("A", "B"); classReferencesGraph.addEdge("B", "C"); classReferencesGraph.addEdge("C", "A"); - Map> cyclesForEveryVertexMap = + Map> cyclesForEveryVertexMap = sutCircularReferenceChecker.detectCycles(classReferencesGraph); assertEquals(3, cyclesForEveryVertexMap.size()); } @@ -35,7 +35,7 @@ public void detectCyclesTest() { @DisplayName("Create graph image in given outputDirectory") @Test public void createImageTest() throws IOException { - Graph classReferencesGraph = new DefaultDirectedGraph<>(DefaultEdge.class); + Graph classReferencesGraph = new DefaultDirectedGraph<>(DefaultWeightedEdge.class); classReferencesGraph.addVertex("A"); classReferencesGraph.addVertex("B"); classReferencesGraph.addVertex("C"); diff --git a/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java b/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java index 34e3c428..f21824ad 100644 --- a/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java +++ b/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java @@ -5,7 +5,7 @@ import java.io.File; import java.io.IOException; import org.jgrapht.Graph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,7 +25,7 @@ public void parseSourceDirectoryEmptyTest() { @Test public void parseSourceDirectoryTest() throws IOException { File srcDirectory = new File("src/test/resources/javaSrcDirectory"); - Graph classReferencesGraph = + Graph classReferencesGraph = sutJavaProjectParser.getClassReferences(srcDirectory.getAbsolutePath()); assertNotNull(classReferencesGraph); assertEquals(5, classReferencesGraph.vertexSet().size()); @@ -42,5 +42,7 @@ public void parseSourceDirectoryTest() throws IOException { assertTrue(classReferencesGraph.containsEdge("D", "A")); assertTrue(classReferencesGraph.containsEdge("D", "C")); assertTrue(classReferencesGraph.containsEdge("E", "D")); + DefaultWeightedEdge edge = classReferencesGraph.getEdge("E", "D"); + assertEquals(2, classReferencesGraph.getEdgeWeight(edge)); } } diff --git a/circular-reference-detector/src/test/resources/javaSrcDirectory/com/ideacrest/parser/testclasses/E.java b/circular-reference-detector/src/test/resources/javaSrcDirectory/com/ideacrest/parser/testclasses/E.java index 5b9fb6e5..f40cd890 100644 --- a/circular-reference-detector/src/test/resources/javaSrcDirectory/com/ideacrest/parser/testclasses/E.java +++ b/circular-reference-detector/src/test/resources/javaSrcDirectory/com/ideacrest/parser/testclasses/E.java @@ -2,4 +2,5 @@ public class E { D d; + D d2; } diff --git a/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java b/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java index e94035f8..a6ece7f9 100644 --- a/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java +++ b/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java @@ -13,6 +13,7 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.sourceforge.pmd.*; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -29,7 +30,7 @@ import org.jgrapht.alg.flow.GusfieldGomoryHuCutTree; import org.jgrapht.graph.AsSubgraph; import org.jgrapht.graph.AsUndirectedGraph; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; @Slf4j public class CostBenefitCalculator { @@ -43,6 +44,9 @@ public class CostBenefitCalculator { private final ChangePronenessRanker changePronenessRanker; private final JavaProjectParser javaProjectParser = new JavaProjectParser(); + @Getter + private Graph classReferencesGraph; + public CostBenefitCalculator(String repositoryPath) { this.repositoryPath = repositoryPath; @@ -65,15 +69,15 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re List rankedCycles = new ArrayList<>(); try { Map classNamesAndPaths = getClassNamesAndPaths(); - Graph classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath); + classReferencesGraph = javaProjectParser.getClassReferences(repositoryPath); CircularReferenceChecker circularReferenceChecker = new CircularReferenceChecker(); - Map> cyclesForEveryVertexMap = + Map> cyclesForEveryVertexMap = circularReferenceChecker.detectCycles(classReferencesGraph); cyclesForEveryVertexMap.forEach((vertex, subGraph) -> { int vertexCount = subGraph.vertexSet().size(); int edgeCount = subGraph.edgeSet().size(); double minCut = 0; - Set minCutEdges = null; + Set minCutEdges; if (vertexCount > 1 && edgeCount > 1 && !isDuplicateSubGraph(subGraph, vertex)) { if (renderImages) { try { @@ -86,17 +90,24 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re renderedSubGraphs.put(vertex, subGraph); log.info("Vertex: " + vertex + " vertex count: " + vertexCount + " edge count: " + edgeCount); - GusfieldGomoryHuCutTree gusfieldGomoryHuCutTree = + GusfieldGomoryHuCutTree gusfieldGomoryHuCutTree = new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph)); minCut = gusfieldGomoryHuCutTree.calculateMinCut(); log.info("Min cut weight: " + minCut); minCutEdges = gusfieldGomoryHuCutTree.getCutEdges(); log.info("Minimum Cut Edges:"); - for (DefaultEdge minCutEdge : minCutEdges) { + for (DefaultWeightedEdge minCutEdge : minCutEdges) { log.info(minCutEdge.toString()); } + log.info("All edge weights:"); + for (DefaultWeightedEdge weightedEdge : + gusfieldGomoryHuCutTree.getGomoryHuTree().edgeSet()) { + log.info(weightedEdge.toString() + ":" + + gusfieldGomoryHuCutTree.getGomoryHuTree().getEdgeWeight(weightedEdge)); + } + List cycleNodes = subGraph.vertexSet().stream() .map(classInCycle -> new CycleNode(classInCycle, classNamesAndPaths.get(classInCycle))) .collect(Collectors.toList()); @@ -148,7 +159,7 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re return rankedCycles; } - private boolean isDuplicateSubGraph(AsSubgraph subGraph, String vertex) { + private boolean isDuplicateSubGraph(AsSubgraph subGraph, String vertex) { if (!renderedSubGraphs.isEmpty()) { for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) { if (renderedSubGraph.vertexSet().size() == subGraph.vertexSet().size() diff --git a/cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedCycle.java b/cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedCycle.java index 44b5bca0..8cce9146 100644 --- a/cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedCycle.java +++ b/cost-benefit-calculator/src/main/java/org/hjug/cbc/RankedCycle.java @@ -5,7 +5,7 @@ import java.util.Set; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; @Data @Slf4j @@ -15,9 +15,9 @@ public class RankedCycle { private final Integer changePronenessRankSum; private final Set vertexSet; - private final Set edgeSet; + private final Set edgeSet; private final double minCutCount; - private final Set minCutEdges; + private final Set minCutEdges; private final List cycleNodes; private float rawPriority; @@ -30,9 +30,9 @@ public RankedCycle( String cycleName, Integer changePronenessRankSum, Set vertexSet, - Set edgeSet, + Set edgeSet, double minCutCount, - Set minCutEdges, + Set minCutEdges, List cycleNodes) { this.cycleNodes = cycleNodes; this.cycleName = cycleName; diff --git a/report/src/main/java/org/hjug/refactorfirst/report/SimpleHtmlReport.java b/report/src/main/java/org/hjug/refactorfirst/report/SimpleHtmlReport.java index b8a6c5b9..74a69244 100644 --- a/report/src/main/java/org/hjug/refactorfirst/report/SimpleHtmlReport.java +++ b/report/src/main/java/org/hjug/refactorfirst/report/SimpleHtmlReport.java @@ -17,7 +17,8 @@ import org.hjug.cbc.RankedCycle; import org.hjug.cbc.RankedDisharmony; import org.hjug.git.GitLogReader; -import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.Graph; +import org.jgrapht.graph.DefaultWeightedEdge; /** * Strictly HTML report that contains no JavaScript @@ -86,6 +87,8 @@ public class SimpleHtmlReport { // public final String[] classCycleTableHeadings = {"Classes", "Relationships", "Min Cut Edges"}; public final String[] classCycleTableHeadings = {"Classes", "Relationships"}; + private Graph classGraph; + public void execute( boolean showDetails, String projectName, String projectVersion, String outputDirectory, File baseDir) { @@ -162,8 +165,9 @@ public void execute( List rankedCBODisharmonies = costBenefitCalculator.calculateCBOCostBenefitValues(); List rankedCycles = runCycleAnalysis(costBenefitCalculator, outputDirectory); + classGraph = costBenefitCalculator.getClassReferencesGraph(); - if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty()) { + if (rankedGodClassDisharmonies.isEmpty() && rankedCBODisharmonies.isEmpty() && rankedCycles.isEmpty()) { stringBuilder .append("Congratulations! ") .append(projectName) @@ -257,8 +261,8 @@ private void renderCycles( stringBuilder.append(""); StringBuilder edgesToCut = new StringBuilder(); - for (DefaultEdge minCutEdge : rankedCycle.getMinCutEdges()) { - edgesToCut.append(minCutEdge.toString()); + for (DefaultWeightedEdge minCutEdge : rankedCycle.getMinCutEdges()) { + edgesToCut.append(minCutEdge + ":" + (int) classGraph.getEdgeWeight(minCutEdge)); edgesToCut.append("
"); } @@ -322,14 +326,18 @@ private void renderCycleTable( stringBuilder.append(""); drawTableCell(vertex, stringBuilder); StringBuilder edges = new StringBuilder(); - for (org.jgrapht.graph.DefaultEdge edge : cycle.getEdgeSet()) { + for (org.jgrapht.graph.DefaultWeightedEdge edge : cycle.getEdgeSet()) { if (edge.toString().startsWith("(" + vertex + " :")) { if (cycle.getMinCutEdges().contains(edge)) { edges.append(""); - edges.append(edge + "*"); + edges.append(edge); + edges.append(":") + .append((int) classGraph.getEdgeWeight(edge)) + .append("*"); edges.append(""); } else { edges.append(edge); + edges.append(":").append((int) classGraph.getEdgeWeight(edge)); } edges.append("
"); From ea817cd7fc2170f4f1c5f81564621a008fba5341 Mon Sep 17 00:00:00 2001 From: Jim Bethancourt Date: Sun, 22 Sep 2024 13:06:55 -0500 Subject: [PATCH 2/2] #92 Correctly capturing edge weights Now correctly capturing edge weights that represent the number of field declarations and method argument formal parameter type declarations of one class in another. --- .../org/hjug/parser/JavaProjectParser.java | 47 ++++++++----------- .../hjug/parser/JavaProjectParserTests.java | 15 ++++-- .../org/hjug/cbc/CostBenefitCalculator.java | 21 ++------- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java b/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java index 135fef3c..51c1700f 100644 --- a/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java +++ b/circular-reference-detector/src/main/java/org/hjug/parser/JavaProjectParser.java @@ -15,8 +15,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.jgrapht.Graph; +import org.jgrapht.graph.DefaultDirectedWeightedGraph; import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.builder.GraphTypeBuilder; public class JavaProjectParser { @@ -28,12 +28,7 @@ public class JavaProjectParser { */ public Graph getClassReferences(String srcDirectory) throws IOException { Graph classReferencesGraph = - GraphTypeBuilder.directed() - .allowingMultipleEdges(false) - .allowingSelfLoops(true) - .edgeClass(DefaultWeightedEdge.class) - .weighted(true) - .buildGraph(); + new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class); if (srcDirectory == null || srcDirectory.isEmpty()) { throw new IllegalArgumentException(); } else { @@ -42,20 +37,20 @@ public Graph getClassReferences(String srcDirectory filesStream .filter(path -> path.getFileName().toString().endsWith(".java")) .forEach(path -> { - Set varTypes = getInstanceVarTypes(classNames, path.toFile()); - varTypes.addAll(getMethodTypes(classNames, path.toFile())); - if (!varTypes.isEmpty()) { + List types = getInstanceVarTypes(classNames, path.toFile()); + types.addAll(getMethodArgumentTypes(classNames, path.toFile())); + if (!types.isEmpty()) { String className = getClassName(path.getFileName().toString()); classReferencesGraph.addVertex(className); - varTypes.forEach(classReferencesGraph::addVertex); - varTypes.forEach(varType -> { - DefaultWeightedEdge weightedEdge = classReferencesGraph.addEdge(className, varType); - - // not sure why some edges are null, but let's ignore them for now - if (null != weightedEdge) { + types.forEach(classReferencesGraph::addVertex); + types.forEach(type -> { + if (!classReferencesGraph.containsEdge(className, type)) { + classReferencesGraph.addEdge(className, type); + } else { + DefaultWeightedEdge edge = classReferencesGraph.getEdge(className, type); classReferencesGraph.setEdgeWeight( - weightedEdge, classReferencesGraph.getEdgeWeight(weightedEdge) + 1); + edge, classReferencesGraph.getEdgeWeight(edge) + 1); } }); } @@ -65,10 +60,6 @@ public Graph getClassReferences(String srcDirectory } } - for (DefaultWeightedEdge weightedEdge : classReferencesGraph.edgeSet()) { - System.out.println(weightedEdge.toString() + ":" + classReferencesGraph.getEdgeWeight(weightedEdge)); - } - return classReferencesGraph; } @@ -78,7 +69,7 @@ public Graph getClassReferences(String srcDirectory * @param file * @return */ - private Set getInstanceVarTypes(List classNamesToFilterBy, File javaSrcFile) { + private List getInstanceVarTypes(List classNamesToFilterBy, File javaSrcFile) { CompilationUnit compilationUnit; try { compilationUnit = StaticJavaParser.parse(javaSrcFile); @@ -87,11 +78,11 @@ private Set getInstanceVarTypes(List classNamesToFilterBy, File .filter(v -> !v.isPrimitiveType()) .map(Object::toString) .filter(classNamesToFilterBy::contains) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } catch (FileNotFoundException e) { e.printStackTrace(); } - return new HashSet<>(); + return new ArrayList<>(); } /** @@ -100,7 +91,7 @@ private Set getInstanceVarTypes(List classNamesToFilterBy, File * @param file * @return */ - private Set getMethodTypes(List classNamesToFilterBy, File javaSrcFile) { + private List getMethodArgumentTypes(List classNamesToFilterBy, File javaSrcFile) { CompilationUnit compilationUnit; try { compilationUnit = StaticJavaParser.parse(javaSrcFile); @@ -108,15 +99,15 @@ private Set getMethodTypes(List classNamesToFilterBy, File javaS .flatMap(f -> f.getParameters().stream() .map(Parameter::getType) .filter(type -> !type.isPrimitiveType()) - .collect(Collectors.toSet()) + .collect(Collectors.toList()) .stream()) .map(Object::toString) .filter(classNamesToFilterBy::contains) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } catch (FileNotFoundException e) { e.printStackTrace(); } - return new HashSet<>(); + return new ArrayList<>(); } /** diff --git a/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java b/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java index f21824ad..c09f47e1 100644 --- a/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java +++ b/circular-reference-detector/src/test/java/org/hjug/parser/JavaProjectParserTests.java @@ -16,14 +16,14 @@ class JavaProjectParserTests { @DisplayName("When source directory input param is empty or null throw IllegalArgumentException.") @Test - public void parseSourceDirectoryEmptyTest() { + void parseSourceDirectoryEmptyTest() { Assertions.assertThrows(IllegalArgumentException.class, () -> sutJavaProjectParser.getClassReferences("")); Assertions.assertThrows(IllegalArgumentException.class, () -> sutJavaProjectParser.getClassReferences(null)); } @DisplayName("Given a valid source directory input parameter return a valid graph.") @Test - public void parseSourceDirectoryTest() throws IOException { + void parseSourceDirectoryTest() throws IOException { File srcDirectory = new File("src/test/resources/javaSrcDirectory"); Graph classReferencesGraph = sutJavaProjectParser.getClassReferences(srcDirectory.getAbsolutePath()); @@ -42,7 +42,14 @@ public void parseSourceDirectoryTest() throws IOException { assertTrue(classReferencesGraph.containsEdge("D", "A")); assertTrue(classReferencesGraph.containsEdge("D", "C")); assertTrue(classReferencesGraph.containsEdge("E", "D")); - DefaultWeightedEdge edge = classReferencesGraph.getEdge("E", "D"); - assertEquals(2, classReferencesGraph.getEdgeWeight(edge)); + + // confirm edge weight calculations + assertEquals(1, getEdgeWeight(classReferencesGraph, "A", "B")); + assertEquals(2, getEdgeWeight(classReferencesGraph, "E", "D")); + } + + private static double getEdgeWeight( + Graph classReferencesGraph, String sourceVertex, String targetVertex) { + return classReferencesGraph.getEdgeWeight(classReferencesGraph.getEdge(sourceVertex, targetVertex)); } } diff --git a/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java b/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java index a6ece7f9..a2bc7ff3 100644 --- a/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java +++ b/cost-benefit-calculator/src/main/java/org/hjug/cbc/CostBenefitCalculator.java @@ -35,7 +35,7 @@ @Slf4j public class CostBenefitCalculator { - private final Map renderedSubGraphs = new HashMap<>(); + private final Map> renderedSubGraphs = new HashMap<>(); private Report report; private String repositoryPath; @@ -93,21 +93,8 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re GusfieldGomoryHuCutTree gusfieldGomoryHuCutTree = new GusfieldGomoryHuCutTree<>(new AsUndirectedGraph<>(subGraph)); minCut = gusfieldGomoryHuCutTree.calculateMinCut(); - log.info("Min cut weight: " + minCut); minCutEdges = gusfieldGomoryHuCutTree.getCutEdges(); - log.info("Minimum Cut Edges:"); - for (DefaultWeightedEdge minCutEdge : minCutEdges) { - log.info(minCutEdge.toString()); - } - - log.info("All edge weights:"); - for (DefaultWeightedEdge weightedEdge : - gusfieldGomoryHuCutTree.getGomoryHuTree().edgeSet()) { - log.info(weightedEdge.toString() + ":" - + gusfieldGomoryHuCutTree.getGomoryHuTree().getEdgeWeight(weightedEdge)); - } - List cycleNodes = subGraph.vertexSet().stream() .map(classInCycle -> new CycleNode(classInCycle, classNamesAndPaths.get(classInCycle))) .collect(Collectors.toList()); @@ -120,8 +107,8 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re } for (ScmLogInfo changeRank : changeRanks) { - CycleNode cn = cycleNodeMap.get(changeRank.getPath()); - cn.setScmLogInfo(changeRank); + CycleNode cycleNode = cycleNodeMap.get(changeRank.getPath()); + cycleNode.setScmLogInfo(changeRank); } // sum change proneness ranks @@ -161,7 +148,7 @@ public List runCycleAnalysis(String outputDirectoryPath, boolean re private boolean isDuplicateSubGraph(AsSubgraph subGraph, String vertex) { if (!renderedSubGraphs.isEmpty()) { - for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) { + for (AsSubgraph renderedSubGraph : renderedSubGraphs.values()) { if (renderedSubGraph.vertexSet().size() == subGraph.vertexSet().size() && renderedSubGraph.edgeSet().size() == subGraph.edgeSet().size()