]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5672 Don't save DSM when there is no dependency or more than 200 components
authorJulien HENRY <julien.henry@sonarsource.com>
Fri, 7 Nov 2014 14:53:07 +0000 (15:53 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 7 Nov 2014 15:15:09 +0000 (16:15 +0100)
15 files changed:
sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
sonar-graph/src/main/java/org/sonar/graph/DirectedGraph.java
sonar-graph/src/main/java/org/sonar/graph/Dsm.java
sonar-graph/src/main/java/org/sonar/graph/DsmScanner.java
sonar-graph/src/main/java/org/sonar/graph/FeedbackCycle.java
sonar-graph/src/main/java/org/sonar/graph/IncrementalCyclesAndFESSolver.java
sonar-graph/src/main/java/org/sonar/graph/MinimumFeedbackEdgeSetSolver.java

index 0446d61fd38ce1e0ea632a06a6e39b2aa2244430..95c3da4ae8c8ca842bd05c5a75f012099048a756 100644 (file)
  */
 package org.sonar.batch.design;
 
-import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.design.Dependency;
 import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.PersistenceMode;
-import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.graph.Cycle;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
 import org.sonar.graph.Edge;
 import org.sonar.graph.IncrementalCyclesAndFESSolver;
 import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
-public class DirectoryDsmDecorator implements Decorator {
-
-  private SonarIndex index;
+public class DirectoryDsmDecorator extends DsmDecorator {
 
   public DirectoryDsmDecorator(SonarIndex index) {
-    this.index = index;
+    super(index);
   }
 
   @Override
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
-  }
-
-  @Override
-  public void decorate(final Resource resource, DecoratorContext context) {
-    if (shouldDecorateResource(resource, context)) {
-      List<DecoratorContext> fileContexts = context.getChildren();
-      List<Resource> files = new ArrayList<Resource>(fileContexts.size());
-      for (DecoratorContext decoratorContext : fileContexts) {
-        files.add(decoratorContext.getResource());
-      }
-
-      IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(index, files);
-      Set<Cycle> cycles = cycleDetector.getCycles();
-
-      MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
-      Set<Edge> feedbackEdges = solver.getEdges();
-      int tangles = solver.getWeightOfFeedbackEdgeSet();
-
-      savePositiveMeasure(context, CoreMetrics.FILE_CYCLES, cycles.size());
-      savePositiveMeasure(context, CoreMetrics.FILE_FEEDBACK_EDGES, feedbackEdges.size());
-      savePositiveMeasure(context, CoreMetrics.FILE_TANGLES, tangles);
-      savePositiveMeasure(context, CoreMetrics.FILE_EDGES_WEIGHT, getEdgesWeight(files));
-
-      Dsm<Resource> dsm = getDsm(files, feedbackEdges);
-      saveDsm(context, dsm);
-    }
-  }
-
-  private void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
-    if (value >= 0.0) {
-      context.saveMeasure(new Measure(metric, value));
+  protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
+    List<DecoratorContext> fileContexts = context.getChildren();
+    List<Resource> files = new ArrayList<Resource>(fileContexts.size());
+    for (DecoratorContext decoratorContext : fileContexts) {
+      files.add(decoratorContext.getResource());
     }
+    return files;
   }
 
-  private int getEdgesWeight(Collection<Resource> sourceCodes) {
-    List<Dependency> edges = getEdges(sourceCodes);
-    int total = 0;
-    for (Dependency edge : edges) {
-      total += edge.getWeight();
-    }
-    return total;
-  }
+  @Override
+  protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+    IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(getIndex(), children);
+    Set<Cycle> cycles = cycleDetector.getCycles();
 
-  public List<Dependency> getEdges(Collection<Resource> vertices) {
-    List<Dependency> result = new ArrayList<Dependency>();
-    for (Resource vertice : vertices) {
-      Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
-      if (outgoingEdges != null) {
-        result.addAll(outgoingEdges);
-      }
-    }
-    return result;
-  }
+    MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+    Set<Edge> feedbackEdges = solver.getEdges();
+    int tangles = solver.getWeightOfFeedbackEdgeSet();
 
-  private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
-    Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
-    measure.setPersistenceMode(PersistenceMode.DATABASE);
-    context.saveMeasure(measure);
-  }
+    savePositiveMeasure(context, CoreMetrics.FILE_CYCLES, cycles.size());
+    savePositiveMeasure(context, CoreMetrics.FILE_FEEDBACK_EDGES, feedbackEdges.size());
+    savePositiveMeasure(context, CoreMetrics.FILE_TANGLES, tangles);
+    savePositiveMeasure(context, CoreMetrics.FILE_EDGES_WEIGHT, getEdgesWeight(children));
 
-  private Dsm<Resource> getDsm(Collection<Resource> files, Set<Edge> feedbackEdges) {
-    Dsm<Resource> dsm = new Dsm<Resource>(index, files, feedbackEdges);
-    DsmTopologicalSorter.sort(dsm);
-    return dsm;
+    return feedbackEdges;
   }
 
-  private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
-    return ResourceUtils.isDirectory(resource) && context.getMeasure(CoreMetrics.DEPENDENCY_MATRIX) == null;
+  @Override
+  protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+    return ResourceUtils.isDirectory(resource);
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
new file mode 100644 (file)
index 0000000..3569ba9
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.design;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmTopologicalSorter;
+import org.sonar.graph.Edge;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public abstract class DsmDecorator implements Decorator {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DsmDecorator.class);
+
+  private static final int MAX_DSM_DIMENSION = 200;
+  private SonarIndex index;
+
+  public DsmDecorator(SonarIndex index) {
+    this.index = index;
+  }
+
+  public final SonarIndex getIndex() {
+    return index;
+  }
+
+  @Override
+  public final boolean shouldExecuteOnProject(Project project) {
+    return true;
+  }
+
+  @Override
+  public final void decorate(final Resource resource, DecoratorContext context) {
+    if (shouldDecorateResource(resource, context)) {
+      List<Resource> children = getChildren(resource, context);
+      if (children.isEmpty()) {
+        return;
+      }
+      Set<Edge> feedbackEdges = doProcess(children, context);
+
+      if (children.size() > MAX_DSM_DIMENSION) {
+        LOG.warn("Too many components under resource '" + resource.getName() + "'. DSM will not be displayed.");
+        return;
+      }
+      Dsm<Resource> dsm = getDsm(children, feedbackEdges);
+      // Optimization, don't save DSM if there is no dependency at all
+      if (dsm.hasAtLeastOneDependency()) {
+        saveDsm(context, dsm);
+      }
+    }
+  }
+
+  protected abstract boolean shouldDecorateResource(Resource resource, DecoratorContext context);
+
+  protected abstract List<Resource> getChildren(Resource resource, DecoratorContext context);
+
+  protected abstract Set<Edge> doProcess(List<Resource> children, DecoratorContext context);
+
+  protected final void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
+    Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
+    measure.setPersistenceMode(PersistenceMode.DATABASE);
+    context.saveMeasure(measure);
+  }
+
+  protected final Dsm<Resource> getDsm(Collection<Resource> children, Set<Edge> feedbackEdges) {
+    Dsm<Resource> dsm = new Dsm<Resource>(index, children, feedbackEdges);
+    DsmTopologicalSorter.sort(dsm);
+    return dsm;
+  }
+
+  protected final void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
+    if (value >= 0.0) {
+      context.saveMeasure(new Measure(metric, value));
+    }
+  }
+
+  protected final int getEdgesWeight(Collection<Resource> sourceCodes) {
+    List<Dependency> edges = getEdges(sourceCodes);
+    int total = 0;
+    for (Dependency edge : edges) {
+      total += edge.getWeight();
+    }
+    return total;
+  }
+
+  protected final List<Dependency> getEdges(Collection<Resource> vertices) {
+    List<Dependency> result = new ArrayList<Dependency>();
+    for (Resource vertice : vertices) {
+      Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
+      if (outgoingEdges != null) {
+        result.addAll(outgoingEdges);
+      }
+    }
+    return result;
+  }
+}
index f9ae4de6a5004149bc9ac9d177168c4e08d8d1b9..66471955795a100601f3714c4e2ea279e2d18c96 100644 (file)
 package org.sonar.batch.design;
 
 import com.google.common.collect.Lists;
-import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.graph.Cycle;
 import org.sonar.graph.CycleDetector;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
 import org.sonar.graph.Edge;
 import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
 
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
-public class ProjectDsmDecorator implements Decorator {
-
-  private SonarIndex index;
+public class ProjectDsmDecorator extends DsmDecorator {
 
   public ProjectDsmDecorator(SonarIndex index) {
-    this.index = index;
-  }
-
-  @Override
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
-  }
-
-  @Override
-  public void decorate(final Resource resource, DecoratorContext context) {
-    if (shouldDecorateResource(resource, context)) {
-      Collection<Resource> subProjects = getSubProjects((Project) resource);
-
-      if (!subProjects.isEmpty()) {
-        Dsm<Resource> dsm = getDsm(subProjects);
-        saveDsm(context, dsm);
-      }
-    }
-  }
-
-  private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
-    Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
-    measure.setPersistenceMode(PersistenceMode.DATABASE);
-    context.saveMeasure(measure);
-  }
-
-  private Dsm<Resource> getDsm(Collection<Resource> subProjects) {
-    CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(index, subProjects);
-    Set<Cycle> cycles = cycleDetector.getCycles();
-
-    MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
-    Set<Edge> feedbackEdges = solver.getEdges();
-
-    Dsm<Resource> dsm = new Dsm<Resource>(index, subProjects, feedbackEdges);
-    DsmTopologicalSorter.sort(dsm);
-    return dsm;
+    super(index);
   }
 
   /**
    * sub-projects, including all descendants but not only direct children
    */
-  private Collection<Resource> getSubProjects(final Project project) {
+  @Override
+  protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
     List<Resource> subProjects = Lists.newArrayList();
-    addSubProjects(project, subProjects);
+    addSubProjects((Project) resource, subProjects);
     return subProjects;
   }
 
   private void addSubProjects(Project project, List<Resource> subProjects) {
     for (Project subProject : project.getModules()) {
-      Project indexedSubProject = index.getResource(subProject);
+      Project indexedSubProject = getIndex().getResource(subProject);
       if (indexedSubProject != null) {
         subProjects.add(indexedSubProject);
       }
@@ -102,7 +59,17 @@ public class ProjectDsmDecorator implements Decorator {
     }
   }
 
-  private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+  @Override
+  protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+    CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(getIndex(), children);
+    Set<Cycle> cycles = cycleDetector.getCycles();
+
+    MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+    return solver.getEdges();
+  }
+
+  @Override
+  protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
     // Should not execute on views
     return (ResourceUtils.isRootProject(resource) || ResourceUtils.isModuleProject(resource))
       && !((Project) resource).getModules().isEmpty();
index 3d850f3ab50500a0ce6687aa3a2cc267a1cf7846..9e03a13c7df6164aa1be0de076a61f41ebe9ac26 100644 (file)
  */
 package org.sonar.batch.design;
 
-import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.design.Dependency;
 import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.measures.PersistenceMode;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
 import org.sonar.graph.Cycle;
-import org.sonar.graph.Dsm;
-import org.sonar.graph.DsmTopologicalSorter;
 import org.sonar.graph.Edge;
 import org.sonar.graph.IncrementalCyclesAndFESSolver;
 import org.sonar.graph.MinimumFeedbackEdgeSetSolver;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
-public class SubProjectDsmDecorator implements Decorator {
-
-  private SonarIndex index;
+public class SubProjectDsmDecorator extends DsmDecorator {
 
   public SubProjectDsmDecorator(SonarIndex index) {
-    this.index = index;
-  }
-
-  @Override
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
+    super(index);
   }
 
   @Override
-  public void decorate(final Resource resource, DecoratorContext context) {
-    if (shouldDecorateResource(resource, context)) {
-      List<DecoratorContext> directoryContexts = context.getChildren();
-      List<Resource> directories = new ArrayList<Resource>(directoryContexts.size());
-      for (DecoratorContext decoratorContext : directoryContexts) {
-        directories.add(decoratorContext.getResource());
-      }
-
-      IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(index, directories);
-      Set<Cycle> cycles = cycleDetector.getCycles();
-
-      MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
-      Set<Edge> feedbackEdges = solver.getEdges();
-      int tangles = solver.getWeightOfFeedbackEdgeSet();
-
-      savePositiveMeasure(context, CoreMetrics.DIRECTORY_CYCLES, cycles.size());
-      savePositiveMeasure(context, CoreMetrics.DIRECTORY_FEEDBACK_EDGES, feedbackEdges.size());
-      savePositiveMeasure(context, CoreMetrics.DIRECTORY_TANGLES, tangles);
-      savePositiveMeasure(context, CoreMetrics.DIRECTORY_EDGES_WEIGHT, getEdgesWeight(directories));
-
-      Dsm<Resource> dsm = getDsm(directories, feedbackEdges);
-      saveDsm(context, dsm);
-    }
-  }
-
-  private void savePositiveMeasure(DecoratorContext context, Metric<Integer> metric, double value) {
-    if (value >= 0.0) {
-      context.saveMeasure(new Measure(metric, value));
+  protected List<Resource> getChildren(Resource resource, DecoratorContext context) {
+    List<DecoratorContext> directoryContexts = context.getChildren();
+    List<Resource> directories = new ArrayList<Resource>(directoryContexts.size());
+    for (DecoratorContext decoratorContext : directoryContexts) {
+      directories.add(decoratorContext.getResource());
     }
+    return directories;
   }
 
-  private int getEdgesWeight(Collection<Resource> sourceCodes) {
-    List<Dependency> edges = getEdges(sourceCodes);
-    int total = 0;
-    for (Dependency edge : edges) {
-      total += edge.getWeight();
-    }
-    return total;
-  }
-
-  public List<Dependency> getEdges(Collection<Resource> vertices) {
-    List<Dependency> result = new ArrayList<Dependency>();
-    for (Resource vertice : vertices) {
-      Collection<Dependency> outgoingEdges = index.getOutgoingEdges(vertice);
-      if (outgoingEdges != null) {
-        result.addAll(outgoingEdges);
-      }
-    }
-    return result;
-  }
-
-  private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
-    Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
-    measure.setPersistenceMode(PersistenceMode.DATABASE);
-    context.saveMeasure(measure);
-  }
-
-  private Dsm<Resource> getDsm(Collection<Resource> directories, Set<Edge> feedbackEdges) {
-    Dsm<Resource> dsm = new Dsm<Resource>(index, directories, feedbackEdges);
-    DsmTopologicalSorter.sort(dsm);
-    return dsm;
+  @Override
+  protected Set<Edge> doProcess(List<Resource> children, DecoratorContext context) {
+    IncrementalCyclesAndFESSolver<Resource> cycleDetector = new IncrementalCyclesAndFESSolver<Resource>(getIndex(), children);
+    Set<Cycle> cycles = cycleDetector.getCycles();
+
+    MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+    Set<Edge> feedbackEdges = solver.getEdges();
+    int tangles = solver.getWeightOfFeedbackEdgeSet();
+
+    savePositiveMeasure(context, CoreMetrics.DIRECTORY_CYCLES, cycles.size());
+    savePositiveMeasure(context, CoreMetrics.DIRECTORY_FEEDBACK_EDGES, feedbackEdges.size());
+    savePositiveMeasure(context, CoreMetrics.DIRECTORY_TANGLES, tangles);
+    savePositiveMeasure(context, CoreMetrics.DIRECTORY_EDGES_WEIGHT, getEdgesWeight(children));
+    return feedbackEdges;
   }
 
-  private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+  @Override
+  protected boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
     // Should not execute on views
     return (ResourceUtils.isRootProject(resource) || ResourceUtils.isModuleProject(resource))
       // Only on leaf projects
index 9cc85c83c2bb6048552d5cb12d1439306acdf2cd..b1235f099646b1161121973ac8e4c2f4b869b0f9 100644 (file)
@@ -80,11 +80,11 @@ public class DefaultIndex extends SonarIndex {
 
   // caches
   private Project currentProject;
-  private Map<Resource, Bucket> buckets = Maps.newHashMap();
-  private Map<String, Bucket> bucketsByDeprecatedKey = Maps.newHashMap();
-  private Set<Dependency> dependencies = Sets.newHashSet();
-  private Map<Resource, Map<Resource, Dependency>> outgoingDependenciesByResource = Maps.newHashMap();
-  private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newHashMap();
+  private Map<Resource, Bucket> buckets = Maps.newLinkedHashMap();
+  private Map<String, Bucket> bucketsByDeprecatedKey = Maps.newLinkedHashMap();
+  private Set<Dependency> dependencies = Sets.newLinkedHashSet();
+  private Map<Resource, Map<Resource, Dependency>> outgoingDependenciesByResource = Maps.newLinkedHashMap();
+  private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newLinkedHashMap();
   private ProjectTree projectTree;
   private final DeprecatedViolations deprecatedViolations;
   private ModuleIssues moduleIssues;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
new file mode 100644 (file)
index 0000000..48a7b81
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DirectoryDsmDecoratorTest {
+
+  private DirectoryDsmDecorator decorator;
+  private Directory dir;
+  private DecoratorContext dirContext;
+  private SonarIndex index;
+  private File file1;
+  private File file2;
+  private DecoratorContext file1Context;
+  private DecoratorContext file2Context;
+
+  @Before
+  public void prepare() {
+    index = mock(SonarIndex.class);
+    decorator = new DirectoryDsmDecorator(index);
+    dir = Directory.create("src");
+    dirContext = mock(DecoratorContext.class);
+
+    file1 = File.create("src/Foo1.java", "Foo1.java", null, false);
+    file1.setId(1);
+    file2 = File.create("src/Foo2.java", "Foo2.java", null, false);
+    file2.setId(2);
+
+    file1Context = mock(DecoratorContext.class);
+    when(file1Context.getResource()).thenReturn(file1);
+    file2Context = mock(DecoratorContext.class);
+    when(file2Context.getResource()).thenReturn(file2);
+
+    when(dirContext.getChildren()).thenReturn(Arrays.asList(file1Context, file2Context));
+
+  }
+
+  @Test
+  public void testDirectoryDsmDecoratorNoExecution() {
+    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+    // Should not execute on project
+    decorator.decorate(new Project("foo"), dirContext);
+
+    // Should not do anything if dir has no files
+    when(dirContext.getChildren()).thenReturn(Collections.emptyList());
+    decorator.decorate(dir, dirContext);
+
+    verify(dirContext, never()).saveMeasure(any(Measure.class));
+  }
+
+  @Test
+  public void testDirectoryDsmDecoratorNoDependency() {
+    decorator.decorate(dir, dirContext);
+
+    verify(dirContext, times(4)).saveMeasure(any(Measure.class));
+
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 0.0));
+  }
+
+  @Test
+  public void testDirectoryDsmDecoratorDependency() {
+    Dependency dependency = new Dependency(file1, file2).setWeight(1).setId(51L);
+    when(index.getEdge(file1, file2)).thenReturn(dependency);
+    when(index.hasEdge(file1, file2)).thenReturn(true);
+    when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency));
+    when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency));
+
+    decorator.decorate(dir, dirContext);
+
+    verify(dirContext, times(5)).saveMeasure(any(Measure.class));
+
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 1.0));
+
+    verify(dirContext).saveMeasure(
+      isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+        "[{\"i\":1,\"n\":\"Foo1.java\",\"q\":\"FIL\",\"v\":[{},{}]},{\"i\":2,\"n\":\"Foo2.java\",\"q\":\"FIL\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+  }
+
+  @Test
+  public void testDirectoryDsmDecoratorNoDSMIfMoreThan200Components() {
+    Dependency dependency = new Dependency(file1, file2).setWeight(1).setId(51L);
+    when(index.getEdge(file1, file2)).thenReturn(dependency);
+    when(index.hasEdge(file1, file2)).thenReturn(true);
+    when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency));
+    when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency));
+
+    List<DecoratorContext> contexts = new ArrayList<DecoratorContext>(201);
+    contexts.add(file1Context);
+    contexts.add(file2Context);
+    for (int i = 0; i < 199; i++) {
+      DecoratorContext fileContext = mock(DecoratorContext.class);
+      when(fileContext.getResource()).thenReturn(File.create("file" + i));
+      contexts.add(fileContext);
+    }
+
+    when(dirContext.getChildren()).thenReturn(contexts);
+
+    decorator.decorate(dir, dirContext);
+
+    verify(dirContext, times(4)).saveMeasure(any(Measure.class));
+
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 1.0));
+  }
+
+  @Test
+  public void testDirectoryDsmDecoratorCycleDependency() {
+    Dependency dependency1to2 = new Dependency(file1, file2).setWeight(1).setId(50L);
+    when(index.getEdge(file1, file2)).thenReturn(dependency1to2);
+    when(index.hasEdge(file1, file2)).thenReturn(true);
+    when(index.getOutgoingEdges(file1)).thenReturn(Arrays.asList(dependency1to2));
+    when(index.getIncomingEdges(file2)).thenReturn(Arrays.asList(dependency1to2));
+    Dependency dependency2to1 = new Dependency(file2, file1).setWeight(2).setId(51L);
+    when(index.getEdge(file2, file1)).thenReturn(dependency2to1);
+    when(index.hasEdge(file2, file1)).thenReturn(true);
+    when(index.getOutgoingEdges(file2)).thenReturn(Arrays.asList(dependency2to1));
+    when(index.getIncomingEdges(file1)).thenReturn(Arrays.asList(dependency2to1));
+
+    decorator.decorate(dir, dirContext);
+
+    verify(dirContext, times(5)).saveMeasure(any(Measure.class));
+
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 1.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 1.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 1.0));
+    verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 3.0));
+
+    verify(dirContext).saveMeasure(
+      isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+        "[{\"i\":2,\"n\":\"Foo2.java\",\"q\":\"FIL\",\"v\":[{},{\"i\":50,\"w\":1}]},{\"i\":1,\"n\":\"Foo1.java\",\"q\":\"FIL\",\"v\":[{\"i\":51,\"w\":2},{}]}]"));
+  }
+
+  Measure isMeasureWithValue(Metric metric, Double value) {
+    return argThat(new IsMeasureWithValue(metric, value));
+  }
+
+  Measure isMeasureWithValue(Metric metric, String data) {
+    return argThat(new IsMeasureWithValue(metric, data));
+  }
+
+  class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+    private Metric metric;
+    private Double value;
+    private String data;
+
+    public IsMeasureWithValue(Metric metric, Double value) {
+      this.metric = metric;
+      this.value = value;
+    }
+
+    public IsMeasureWithValue(Metric metric, String data) {
+      this.metric = metric;
+      this.data = data;
+    }
+
+    public boolean matches(Object m) {
+      return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+    }
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
new file mode 100644 (file)
index 0000000..3850d03
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Project;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ProjectDsmDecoratorTest {
+
+  private ProjectDsmDecorator decorator;
+  private Project root;
+  private DecoratorContext rootContext;
+  private SonarIndex index;
+  private Project module1;
+  private Project module2;
+
+  @Before
+  public void prepare() {
+    index = mock(SonarIndex.class);
+    decorator = new ProjectDsmDecorator(index);
+    root = new Project("root");
+    rootContext = mock(DecoratorContext.class);
+
+    module1 = new Project("module1").setName("Module1").setParent(root);
+    module1.setId(1);
+    when(index.getResource(module1)).thenReturn(module1);
+    module2 = new Project("module2").setName("Module2").setParent(root);
+    module2.setId(2);
+    when(index.getResource(module2)).thenReturn(module2);
+
+    DecoratorContext module1Context = mock(DecoratorContext.class);
+    when(module1Context.getResource()).thenReturn(module1);
+    DecoratorContext module2Context = mock(DecoratorContext.class);
+    when(module2Context.getResource()).thenReturn(module2);
+
+    when(rootContext.getChildren()).thenReturn(Arrays.asList(module1Context, module2Context));
+
+  }
+
+  @Test
+  public void testProjectDsmDecoratorNoExecution() {
+    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+    // Should not execute on directory
+    decorator.decorate(Directory.create("foo"), rootContext);
+    // Should not execute on aggregator projects
+    Project p = new Project("parent");
+    Project child = new Project("child").setParent(p);
+    decorator.decorate(p, rootContext);
+
+    // Should not do anything if module has no dir
+    when(rootContext.getChildren()).thenReturn(Collections.emptyList());
+    decorator.decorate(root, rootContext);
+
+    verify(rootContext, never()).saveMeasure(any(Measure.class));
+  }
+
+  @Test
+  public void testProjectDsmDecoratorNoDependency() {
+    decorator.decorate(root, rootContext);
+
+    verify(rootContext, never()).saveMeasure(any(Measure.class));
+  }
+
+  @Test
+  public void testProjectDsmDecoratorDependency() {
+    Dependency dependency = new Dependency(module1, module2).setWeight(1).setId(51L);
+    when(index.getEdge(module1, module2)).thenReturn(dependency);
+    when(index.hasEdge(module1, module2)).thenReturn(true);
+    when(index.getOutgoingEdges(module1)).thenReturn(Arrays.asList(dependency));
+    when(index.getIncomingEdges(module2)).thenReturn(Arrays.asList(dependency));
+
+    decorator.decorate(root, rootContext);
+
+    verify(rootContext, times(1)).saveMeasure(any(Measure.class));
+
+    verify(rootContext).saveMeasure(
+      isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+        "[{\"i\":1,\"n\":\"Module1\",\"q\":\"BRC\",\"v\":[{},{}]},{\"i\":2,\"n\":\"Module2\",\"q\":\"BRC\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+  }
+
+  Measure isMeasureWithValue(Metric metric, Double value) {
+    return argThat(new IsMeasureWithValue(metric, value));
+  }
+
+  Measure isMeasureWithValue(Metric metric, String data) {
+    return argThat(new IsMeasureWithValue(metric, data));
+  }
+
+  class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+    private Metric metric;
+    private Double value;
+    private String data;
+
+    public IsMeasureWithValue(Metric metric, Double value) {
+      this.metric = metric;
+      this.value = value;
+    }
+
+    public IsMeasureWithValue(Metric metric, String data) {
+      this.metric = metric;
+      this.data = data;
+    }
+
+    public boolean matches(Object m) {
+      return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+    }
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
new file mode 100644 (file)
index 0000000..f4f2b42
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.design;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.lang.ObjectUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Project;
+
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SubProjectDsmDecoratorTest {
+
+  private SubProjectDsmDecorator decorator;
+  private Project module;
+  private DecoratorContext moduleContext;
+  private SonarIndex index;
+  private Directory dir1;
+  private Directory dir2;
+
+  @Before
+  public void prepare() {
+    index = mock(SonarIndex.class);
+    decorator = new SubProjectDsmDecorator(index);
+    module = new Project("foo");
+    moduleContext = mock(DecoratorContext.class);
+
+    dir1 = Directory.create("src/foo1", "foo1");
+    dir1.setId(1);
+    dir2 = Directory.create("src/foo2", "foo2");
+    dir2.setId(2);
+
+    DecoratorContext dir1Context = mock(DecoratorContext.class);
+    when(dir1Context.getResource()).thenReturn(dir1);
+    DecoratorContext dir2Context = mock(DecoratorContext.class);
+    when(dir2Context.getResource()).thenReturn(dir2);
+
+    when(moduleContext.getChildren()).thenReturn(Arrays.asList(dir1Context, dir2Context));
+
+  }
+
+  @Test
+  public void testSubProjectDsmDecoratorNoExecution() {
+    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+
+    // Should not execute on directory
+    decorator.decorate(Directory.create("foo"), moduleContext);
+    // Should not execute on aggregator projects
+    Project p = new Project("parent");
+    Project child = new Project("child").setParent(p);
+    decorator.decorate(p, moduleContext);
+
+    // Should not do anything if module has no dir
+    when(moduleContext.getChildren()).thenReturn(Collections.emptyList());
+    decorator.decorate(module, moduleContext);
+
+    verify(moduleContext, never()).saveMeasure(any(Measure.class));
+  }
+
+  @Test
+  public void testSubProjectDsmDecoratorNoDependency() {
+    decorator.decorate(module, moduleContext);
+
+    verify(moduleContext, times(4)).saveMeasure(any(Measure.class));
+
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_CYCLES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_FEEDBACK_EDGES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_TANGLES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_EDGES_WEIGHT, 0.0));
+  }
+
+  @Test
+  public void testSubProjectDsmDecoratorDependency() {
+    Dependency dependency = new Dependency(dir1, dir2).setWeight(1).setId(51L);
+    when(index.getEdge(dir1, dir2)).thenReturn(dependency);
+    when(index.hasEdge(dir1, dir2)).thenReturn(true);
+    when(index.getOutgoingEdges(dir1)).thenReturn(Arrays.asList(dependency));
+    when(index.getIncomingEdges(dir2)).thenReturn(Arrays.asList(dependency));
+
+    decorator.decorate(module, moduleContext);
+
+    verify(moduleContext, times(5)).saveMeasure(any(Measure.class));
+
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_CYCLES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_FEEDBACK_EDGES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_TANGLES, 0.0));
+    verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_EDGES_WEIGHT, 1.0));
+
+    verify(moduleContext).saveMeasure(
+      isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+        "[{\"i\":1,\"n\":\"src/foo1\",\"q\":\"DIR\",\"v\":[{},{}]},{\"i\":2,\"n\":\"src/foo2\",\"q\":\"DIR\",\"v\":[{\"i\":51,\"w\":1},{}]}]"));
+  }
+
+  Measure isMeasureWithValue(Metric metric, Double value) {
+    return argThat(new IsMeasureWithValue(metric, value));
+  }
+
+  Measure isMeasureWithValue(Metric metric, String data) {
+    return argThat(new IsMeasureWithValue(metric, data));
+  }
+
+  class IsMeasureWithValue extends ArgumentMatcher<Measure> {
+
+    private Metric metric;
+    private Double value;
+    private String data;
+
+    public IsMeasureWithValue(Metric metric, Double value) {
+      this.metric = metric;
+      this.value = value;
+    }
+
+    public IsMeasureWithValue(Metric metric, String data) {
+      this.metric = metric;
+      this.data = data;
+    }
+
+    public boolean matches(Object m) {
+      return ((Measure) m).getMetric().equals(metric) && ObjectUtils.equals(((Measure) m).getValue(), value) && ObjectUtils.equals(((Measure) m).getData(), data);
+    }
+  }
+}
index 82653d849fc3ac609cad8fb30bc935927bfd61fe..ba81aba16b1f425ab4f9d3242ba12bc7be1ba5a1 100644 (file)
@@ -111,10 +111,6 @@ public class MeasureCacheTest {
     assertThat(cache.byResource(p)).hasSize(2);
   }
 
-  /**
-   * This test fails when compression is not enabled for measures. PersistIt seems to be ok with
-   * put but fail when reading value.
-   */
   @Test
   public void should_add_measure_with_big_data() throws Exception {
     MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
index ecbb0fb6d7244daae7a4069b48f8ccc45bf26bbf..c0d63887b8931fd746c917215ec0c7bcb2d93cb4 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.graph;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -32,7 +32,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
   private EdgeFactory<V, E> edgeFactory;
   private Map<V, Map<V, E>> outgoingEdgesByVertex = new HashMap<V, Map<V, E>>();
   private Map<V, Map<V, E>> incomingEdgesByVertex = new HashMap<V, Map<V, E>>();
-  private Set<V> vertices = new HashSet<V>();
+  private Set<V> vertices = new LinkedHashSet<V>();
 
   public DirectedGraph() {
   }
@@ -60,7 +60,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
   private void checkEdgeFacory() {
     if (edgeFactory == null) {
       throw new IllegalStateException(
-          "EdgeFactory<V, E> has not been defined. Please use the 'public E addEdge(V from, V to, E edge)' method.");
+        "EdgeFactory<V, E> has not been defined. Please use the 'public E addEdge(V from, V to, E edge)' method.");
     }
   }
 
@@ -133,7 +133,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
   public Collection<E> getOutgoingEdges(V from) {
     Map<V, E> outgoingEdges = outgoingEdgesByVertex.get(from);
     if (outgoingEdges == null) {
-      return new HashSet<E>();
+      return new LinkedHashSet<E>();
     }
     return outgoingEdges.values();
   }
@@ -142,7 +142,7 @@ public class DirectedGraph<V, E extends Edge<V>> implements DirectedGraphAccesso
   public Collection<E> getIncomingEdges(V to) {
     Map<V, E> incomingEdges = incomingEdgesByVertex.get(to);
     if (incomingEdges == null) {
-      return new HashSet<E>();
+      return new LinkedHashSet<E>();
     }
     return incomingEdges.values();
   }
index fc9f386fff2123e1b767914c3fb89e0ea64ccd36..69e85c251dafc5974d98732c085f739e4a7975c1 100644 (file)
@@ -27,17 +27,17 @@ import java.util.Set;
 
 public class Dsm<V> {
 
-  private V[] vertices;
-  private DsmCell[][] cells;
-  private int dimension;
-  private DirectedGraphAccessor<V, ? extends Edge<V>> graph;
+  private final V[] vertices;
+  private final DsmCell[][] cells;
+  private final int dimension;
+  private final DirectedGraphAccessor<V, ? extends Edge<V>> graph;
+  private boolean atLeastOneDependency = false;
 
   public Dsm(DirectedGraphAccessor<V, ? extends Edge<V>> graph, Collection<V> vertices, Set<Edge> feedbackEdges) {
     this.graph = graph;
     this.dimension = vertices.size();
-    this.cells = new DsmCell[dimension][dimension];
-    initVertices(vertices);
-    initCells(feedbackEdges);
+    this.vertices = initVertices(vertices);
+    this.cells = initCells(feedbackEdges);
   }
 
   public Dsm(DirectedGraphAccessor<V, ? extends Edge<V>> acyclicGraph, Set<Edge> feedbackEdges) {
@@ -48,7 +48,8 @@ public class Dsm<V> {
     this(acyclicGraph, acyclicGraph.getVertices(), Collections.<Edge>emptySet());
   }
 
-  private void initCells(Set<Edge> feedbackEdges) {
+  private DsmCell[][] initCells(Set<Edge> feedbackEdges) {
+    DsmCell[][] cells = new DsmCell[dimension][dimension];
     for (int x = 0; x < dimension; x++) {
       for (int y = 0; y < dimension; y++) {
         V from = vertices[x];
@@ -56,20 +57,23 @@ public class Dsm<V> {
 
         Edge<V> edge = graph.getEdge(from, to);
         if (edge != null) {
+          atLeastOneDependency = true;
           boolean isFeedbackEdge = feedbackEdges.contains(edge);
           cells[x][y] = new DsmCell(edge, isFeedbackEdge);
         }
       }
     }
+    return cells;
   }
 
-  private void initVertices(Collection<V> verticesCol) {
-    this.vertices = (V[]) new Object[dimension];
+  private V[] initVertices(Collection<V> verticesCol) {
+    V[] vertices = (V[]) new Object[dimension];
     int i = 0;
     for (V vertex : verticesCol) {
       vertices[i] = vertex;
       i++;
     }
+    return vertices;
   }
 
   public V getVertex(int rowIndex) {
@@ -159,11 +163,21 @@ public class Dsm<V> {
     return cell != null ? cell : new DsmCell(null, false);
   }
 
+  /**
+   * @since 5.0
+   */
   @CheckForNull
   public DsmCell cell(int x, int y) {
     return cells[x][y];
   }
 
+  /**
+   * @since 5.0
+   */
+  public boolean hasAtLeastOneDependency() {
+    return atLeastOneDependency;
+  }
+
   public V[] getVertices() {
     V[] verticesCopy = (V[]) new Object[vertices.length];
     System.arraycopy(vertices, 0, verticesCopy, 0, vertices.length);
index 4057748c2c6398878ad7c856d73859fbc52cfef8..beb5318f74b8e66ae5c029d0b5770071f03c9c3a 100644 (file)
@@ -26,7 +26,7 @@ import java.io.LineNumberReader;
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.Arrays;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Set;
 
 public final class DsmScanner {
@@ -36,7 +36,7 @@ public final class DsmScanner {
   private static final char FEEDBACK_EDGE_FLAG = '*';
   private final DirectedGraph<String, StringEdge> graph = DirectedGraph.createStringDirectedGraph();
   private String[] vertices;
-  private Set<Edge> feedbackEdges = new HashSet<Edge>();
+  private Set<Edge> feedbackEdges = new LinkedHashSet<Edge>();
 
   private DsmScanner(Reader reader) {
     this.reader = new LineNumberReader(reader);
@@ -49,7 +49,7 @@ public final class DsmScanner {
         readRow(i);
       }
     } catch (IOException e) {
-      throw new RuntimeException("Unable to read DSM content.", e); //NOSONAR
+      throw new RuntimeException("Unable to read DSM content.", e); // NOSONAR
     }
     Dsm<String> dsm = new Dsm<String>(graph, graph.getVertices(), feedbackEdges);
     DsmManualSorter.sort(dsm, Arrays.asList(vertices));
index 81de9669e433efbf2122e3313c448e7c45033acc..7a22fcd0c0da6f32de87edec2e8f025f8dbed437 100644 (file)
 
 package org.sonar.graph;
 
+import com.google.common.collect.LinkedHashMultiset;
+import com.google.common.collect.Multiset;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
-
 /**
  * Note: this class has a natural ordering that is inconsistent with equals
  */
@@ -70,7 +70,7 @@ public final class FeedbackCycle implements Iterable<FeedbackEdge>, Comparable<F
   }
 
   private static Multiset<Edge> createBagWithAllEdgesOfCycles(Set<Cycle> cycles) {
-    Multiset<Edge> edgesBag = HashMultiset.create();
+    Multiset<Edge> edgesBag = LinkedHashMultiset.create();
     for (Cycle cycle : cycles) {
       for (Edge edge : cycle.getEdges()) {
         edgesBag.add(edge);
@@ -94,7 +94,9 @@ public final class FeedbackCycle implements Iterable<FeedbackEdge>, Comparable<F
 
   @Override
   public int compareTo(FeedbackCycle feedbackCycle) {
-    if (getTotalOccurrencesOfEdgesInCycle() < feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {//NOSONAR this class has a natural ordering that is inconsistent with equals
+    if (getTotalOccurrencesOfEdgesInCycle() < feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {// NOSONAR this class has a natural
+                                                                                                  // ordering that is inconsistent with
+                                                                                                  // equals
       return -1;
     }
     if (getTotalOccurrencesOfEdgesInCycle() == feedbackCycle.getTotalOccurrencesOfEdgesInCycle()) {
index 576f461e4c18f4ff49550c6200a8a0b2e670efe0..cd814c615f41edb41c1651ece672316a7a2afbcd 100644 (file)
 package org.sonar.graph;
 
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Set;
 
 public class IncrementalCyclesAndFESSolver<V> {
 
-  private Set<Cycle> cycles = new HashSet<Cycle>();
+  private Set<Cycle> cycles = new LinkedHashSet<Cycle>();
   private long searchCyclesCalls = 0;
   private static final int DEFAULT_MAX_SEARCH_DEPTH_AT_FIRST = 3;
   private static final int DEFAULT_MAX_CYCLES_TO_FOUND_BY_ITERATION = 100;
@@ -37,7 +37,7 @@ public class IncrementalCyclesAndFESSolver<V> {
   }
 
   public IncrementalCyclesAndFESSolver(DirectedGraphAccessor<V, ? extends Edge> graph, Collection<V> vertices, int maxSearchDepthAtFirst,
-      int maxCyclesToFoundByIteration) {
+    int maxCyclesToFoundByIteration) {
 
     iterations++;
     CycleDetector<V> cycleDetector = new CycleDetector<V>(graph, vertices);
index 39a5c374b9f9f003f5d52095854f86b89bc0982b..aaebdced22c16e34cddb5c23949ae64f1244e4c7 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.graph;
 
-import java.util.*;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
 
 public class MinimumFeedbackEdgeSetSolver {
 
@@ -62,7 +64,7 @@ public class MinimumFeedbackEdgeSetSolver {
    * Get edges tagged as feedback.
    */
   public Set<Edge> getEdges() {
-    Set<Edge> edges = new HashSet<Edge>();
+    Set<Edge> edges = new LinkedHashSet<Edge>();
     for (FeedbackEdge fe : feedbackEdges) {
       edges.add(fe.getEdge());
     }
@@ -70,7 +72,7 @@ public class MinimumFeedbackEdgeSetSolver {
   }
 
   private void run() {
-    Set<FeedbackEdge> pendingFeedbackEdges = new HashSet<FeedbackEdge>();
+    Set<FeedbackEdge> pendingFeedbackEdges = new LinkedHashSet<FeedbackEdge>();
     if (cyclesNumber < maxNumberCyclesForSearchingMinimumFeedback) {
       searchFeedbackEdges(0, 0, pendingFeedbackEdges);
     } else {
@@ -79,7 +81,7 @@ public class MinimumFeedbackEdgeSetSolver {
   }
 
   private void lightResearchForFeedbackEdges() {
-    feedbackEdges = new HashSet<FeedbackEdge>();
+    feedbackEdges = new LinkedHashSet<FeedbackEdge>();
     for (FeedbackCycle cycle : feedbackCycles) {
       for (FeedbackEdge edge : cycle) {
         feedbackEdges.add(edge);
@@ -87,7 +89,7 @@ public class MinimumFeedbackEdgeSetSolver {
       }
     }
     minimumFeedbackEdgesWeight = 0;
-    for(FeedbackEdge edge : feedbackEdges) {
+    for (FeedbackEdge edge : feedbackEdges) {
       minimumFeedbackEdgesWeight += edge.getWeight();
     }
   }
@@ -103,7 +105,7 @@ public class MinimumFeedbackEdgeSetSolver {
 
     if (level == cyclesNumber) {
       minimumFeedbackEdgesWeight = pendingWeight;
-      feedbackEdges = new HashSet<FeedbackEdge>(pendingFeedbackEdges);
+      feedbackEdges = new LinkedHashSet<FeedbackEdge>(pendingFeedbackEdges);
       return;
     }