aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-09-17 10:47:32 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2014-09-17 10:48:31 +0200
commit000576a34e031bc58398d85116f1c7bb1f0a200e (patch)
tree7094aeb186a4a9d5566e8e5a367aff2157c561bd /sonar-batch
parent9513bee576bda233b91e6703bf0e40a6c102752c (diff)
downloadsonarqube-000576a34e031bc58398d85116f1c7bb1f0a200e.tar.gz
sonarqube-000576a34e031bc58398d85116f1c7bb1f0a200e.zip
SONAR-5389 New dependency API
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/pom.xml5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java16
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java123
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DirectoryTangleIndexDecorator.java34
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DsmSerializer.java92
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/FileTangleIndexDecorator.java34
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/MavenDependenciesSensor.java265
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java106
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java123
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/TangleIndexDecorator.java91
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java11
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java (renamed from sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java)31
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java30
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/DsmSerializerTest.java60
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/MavenDependenciesSensorTest.java93
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java7
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/design/DsmSerializerTest/dsm.json1
21 files changed, 1113 insertions, 30 deletions
diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml
index 8a44063c151..d63d11bea9a 100644
--- a/sonar-batch/pom.xml
+++ b/sonar-batch/pom.xml
@@ -79,6 +79,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.maven.shared</groupId>
+ <artifactId>maven-dependency-tree</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<scope>provided</scope>
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
index f7acd618a3a..8fcf38baafc 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
@@ -20,6 +20,12 @@
package org.sonar.batch.bootstrap;
import com.google.common.collect.Lists;
+import org.sonar.batch.design.DirectoryDsmDecorator;
+import org.sonar.batch.design.DirectoryTangleIndexDecorator;
+import org.sonar.batch.design.FileTangleIndexDecorator;
+import org.sonar.batch.design.MavenDependenciesSensor;
+import org.sonar.batch.design.ProjectDsmDecorator;
+import org.sonar.batch.design.SubProjectDsmDecorator;
import org.sonar.batch.maven.DefaultMavenPluginExecutor;
import org.sonar.batch.maven.MavenProjectBootstrapper;
import org.sonar.batch.maven.MavenProjectBuilder;
@@ -37,7 +43,15 @@ public class BatchComponents {
public static Collection all() {
List components = Lists.newArrayList(
// Maven
- MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class
+ MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class,
+
+ // Design
+ MavenDependenciesSensor.class,
+ ProjectDsmDecorator.class,
+ SubProjectDsmDecorator.class,
+ DirectoryDsmDecorator.class,
+ DirectoryTangleIndexDecorator.class,
+ FileTangleIndexDecorator.class
);
components.addAll(CorePropertyDefinitions.all());
return components;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java
index b768687b040..0869ee6cde6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java
@@ -24,19 +24,19 @@ import org.sonar.api.batch.fs.InputFile;
public class OutgoingDependency {
private final InputFile to;
- private final String usage;
+ private final int weight;
- public OutgoingDependency(InputFile to, String usage) {
+ public OutgoingDependency(InputFile to, int weight) {
this.to = to;
- this.usage = usage;
+ this.weight = weight;
}
public InputFile to() {
return to;
}
- public String usage() {
- return usage;
+ public int weight() {
+ return weight;
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
new file mode 100644
index 00000000000..0aaa2aa854d
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryDsmDecorator.java
@@ -0,0 +1,123 @@
+/*
+ * 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.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 DirectoryDsmDecorator(SonarIndex index) {
+ this.index = index;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ 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));
+ }
+ }
+
+ 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> files, Set<Edge> feedbackEdges) {
+ Dsm<Resource> dsm = new Dsm<Resource>(index, files, feedbackEdges);
+ DsmTopologicalSorter.sort(dsm);
+ return dsm;
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return ResourceUtils.isDirectory(resource) && context.getMeasure(CoreMetrics.DEPENDENCY_MATRIX) == null;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryTangleIndexDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryTangleIndexDecorator.java
new file mode 100644
index 00000000000..b1fc52c9625
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DirectoryTangleIndexDecorator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.sonar.api.measures.CoreMetrics;
+
+public class DirectoryTangleIndexDecorator extends TangleIndexDecorator {
+
+ public DirectoryTangleIndexDecorator() {
+ super(CoreMetrics.PACKAGE_TANGLES, CoreMetrics.PACKAGE_EDGES_WEIGHT, CoreMetrics.PACKAGE_TANGLE_INDEX);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DsmSerializer.java b/sonar-batch/src/main/java/org/sonar/batch/design/DsmSerializer.java
new file mode 100644
index 00000000000..48da21bd0c2
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DsmSerializer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.sonar.api.design.Dependency;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmCell;
+
+public final class DsmSerializer {
+
+ private Dsm dsm;
+ private StringBuilder json;
+
+ private DsmSerializer(Dsm<Resource> dsm) {
+ this.dsm = dsm;
+ this.json = new StringBuilder();
+ }
+
+ private String serialize() {
+ json.append('[');
+ serializeRows();
+ json.append(']');
+ return json.toString();
+ }
+
+ private void serializeRows() {
+ for (int y = 0; y < dsm.getDimension(); y++) {
+ if (y > 0) {
+ json.append(',');
+ }
+ serializeRow(y);
+ }
+ }
+
+ private void serializeRow(int y) {
+ Resource resource = (Resource) dsm.getVertex(y);
+
+ json.append("{");
+ if (resource != null) {
+ json.append("\"i\":");
+ json.append(resource.getId());
+ json.append(",\"n\":\"");
+ json.append(resource.getName());
+ json.append("\",\"q\":\"");
+ json.append(resource.getQualifier());
+ json.append("\",\"v\":[");
+ for (int x = 0; x < dsm.getDimension(); x++) {
+ if (x > 0) {
+ json.append(',');
+ }
+ serializeCell(y, x);
+ }
+ json.append("]");
+ }
+ json.append("}");
+ }
+
+ private void serializeCell(int y, int x) {
+ DsmCell cell = dsm.getCell(x, y);
+ json.append('{');
+ if (cell.getEdge() != null && cell.getWeight() > 0) {
+ Dependency dep = (Dependency) cell.getEdge();
+ json.append("\"i\":");
+ json.append(dep.getId());
+ json.append(",\"w\":");
+ json.append(cell.getWeight());
+ }
+ json.append('}');
+ }
+
+ public static String serialize(Dsm<Resource> dsm) {
+ return new DsmSerializer(dsm).serialize();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/FileTangleIndexDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/FileTangleIndexDecorator.java
new file mode 100644
index 00000000000..079e3774ebd
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/FileTangleIndexDecorator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.sonar.api.measures.CoreMetrics;
+
+public class FileTangleIndexDecorator extends TangleIndexDecorator {
+
+ public FileTangleIndexDecorator() {
+ super(CoreMetrics.FILE_TANGLES, CoreMetrics.FILE_EDGES_WEIGHT, CoreMetrics.FILE_TANGLE_INDEX);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/MavenDependenciesSensor.java b/sonar-batch/src/main/java/org/sonar/batch/design/MavenDependenciesSensor.java
new file mode 100644
index 00000000000..0f2e7157b22
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/MavenDependenciesSensor.java
@@ -0,0 +1,265 @@
+/*
+ * 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 com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactCollector;
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
+import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.config.Settings;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.Library;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.SonarException;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@SupportedEnvironment("maven")
+public class MavenDependenciesSensor implements Sensor {
+
+ private static final String SONAR_MAVEN_PROJECT_DEPENDENCY = "sonar.maven.projectDependencies";
+
+ private static final Logger LOG = LoggerFactory.getLogger(MavenDependenciesSensor.class);
+
+ private ArtifactRepository localRepository;
+ private ArtifactFactory artifactFactory;
+ private ArtifactMetadataSource artifactMetadataSource;
+ private ArtifactCollector artifactCollector;
+ private DependencyTreeBuilder treeBuilder;
+ private SonarIndex index;
+ private Settings settings;
+
+ public MavenDependenciesSensor(Settings settings, ArtifactRepository localRepository, ArtifactFactory artifactFactory, ArtifactMetadataSource artifactMetadataSource,
+ ArtifactCollector artifactCollector, DependencyTreeBuilder treeBuilder, SonarIndex index) {
+ this.settings = settings;
+ this.localRepository = localRepository;
+ this.artifactFactory = artifactFactory;
+ this.artifactMetadataSource = artifactMetadataSource;
+ this.artifactCollector = artifactCollector;
+ this.index = index;
+ this.treeBuilder = treeBuilder;
+ }
+
+ /**
+ * Used with SQ Maven plugin 2.5+
+ */
+ public MavenDependenciesSensor(Settings settings, SonarIndex index) {
+ this.settings = settings;
+ this.index = index;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ private static class InputDependency {
+
+ private final String key;
+
+ private final String version;
+
+ private String scope;
+
+ List<InputDependency> dependencies = new ArrayList<InputDependency>();
+
+ public InputDependency(String key, String version) {
+ this.key = key;
+ this.version = version;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public String version() {
+ return version;
+ }
+
+ public String scope() {
+ return scope;
+ }
+
+ public InputDependency setScope(String scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ public List<InputDependency> dependencies() {
+ return dependencies;
+ }
+ }
+
+ private static class DependencyDeserializer implements JsonDeserializer<InputDependency> {
+
+ @Override
+ public InputDependency deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
+
+ JsonObject dep = json.getAsJsonObject();
+ String key = dep.get("k").getAsString();
+ String version = dep.get("v").getAsString();
+ InputDependency result = new InputDependency(key, version);
+ result.setScope(dep.get("s").getAsString());
+ JsonElement subDeps = dep.get("d");
+ if (subDeps != null) {
+ JsonArray arrayOfSubDeps = subDeps.getAsJsonArray();
+ for (JsonElement e : arrayOfSubDeps) {
+ result.dependencies().add(deserialize(e, typeOfT, context));
+ }
+ }
+ return result;
+ }
+
+ }
+
+ public void analyse(final Project project, final SensorContext context) {
+ if (settings.hasKey(SONAR_MAVEN_PROJECT_DEPENDENCY)) {
+ LOG.debug("Using dependency provided by property " + SONAR_MAVEN_PROJECT_DEPENDENCY);
+ String depsAsJson = settings.getString(SONAR_MAVEN_PROJECT_DEPENDENCY);
+ Collection<InputDependency> deps;
+ try {
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ gsonBuilder.registerTypeAdapter(InputDependency.class, new DependencyDeserializer());
+ Gson gson = gsonBuilder.create();
+
+ Type collectionType = new TypeToken<Collection<InputDependency>>() {
+ }.getType();
+ deps = gson.fromJson(depsAsJson, collectionType);
+ saveDependencies(project, deps, context);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to deserialize dependency information: " + depsAsJson, e);
+ }
+ } else if (treeBuilder != null) {
+ computeDependencyTree(project, context);
+ }
+ }
+
+ private void computeDependencyTree(final Project project, final SensorContext context) {
+ LOG.warn("Computation of Maven dependencies by SonarQube is deprecated. Please update the version of SonarQube Maven plugin to 2.5+");
+ try {
+ DependencyNode root = treeBuilder.buildDependencyTree(project.getPom(), localRepository, artifactFactory, artifactMetadataSource, null, artifactCollector);
+
+ DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor(new DependencyNodeVisitor() {
+ public boolean visit(DependencyNode node) {
+ return true;
+ }
+
+ public boolean endVisit(DependencyNode node) {
+ if (node.getParent() != null && node.getParent() != node) {
+ saveDependency(node, context);
+ }
+ return true;
+ }
+ });
+
+ // mode verbose OFF : do not show the same lib many times
+ DependencyNodeFilter filter = StateDependencyNodeFilter.INCLUDED;
+
+ CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
+ DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(collectingVisitor, filter);
+ root.accept(firstPassVisitor);
+
+ DependencyNodeFilter secondPassFilter = new AncestorOrSelfDependencyNodeFilter(collectingVisitor.getNodes());
+ visitor = new FilteringDependencyNodeVisitor(visitor, secondPassFilter);
+
+ root.accept(visitor);
+
+ } catch (DependencyTreeBuilderException e) {
+ throw new SonarException("Can not load the graph of dependencies of the project " + project.getKey(), e);
+ }
+ }
+
+ private void saveDependencies(Resource from, Collection<InputDependency> deps, SensorContext context) {
+ for (InputDependency inputDep : deps) {
+ Resource to = toResource(inputDep, context);
+ Dependency dependency = new Dependency(from, to);
+ dependency.setUsage(inputDep.scope());
+ dependency.setWeight(1);
+ context.saveDependency(dependency);
+ if (!inputDep.dependencies().isEmpty()) {
+ saveDependencies(to, inputDep.dependencies(), context);
+ }
+ }
+ }
+
+ private Resource toResource(InputDependency dependency, SensorContext context) {
+ Project project = new Project(dependency.key());
+ Resource result = context.getResource(project);
+ if (result == null || !((Project) result).getAnalysisVersion().equals(dependency.version())) {
+ Library lib = new Library(project.getKey(), dependency.version());
+ context.saveResource(lib);
+ result = context.getResource(lib);
+ }
+ return result;
+ }
+
+ protected void saveDependency(DependencyNode node, SensorContext context) {
+ Resource from = (node.getParent().getParent() == null) ? index.getProject() : toResource(node.getParent().getArtifact(), context);
+ Resource to = toResource(node.getArtifact(), context);
+ Dependency dependency = new Dependency(from, to);
+ dependency.setUsage(node.getArtifact().getScope());
+ dependency.setWeight(1);
+ context.saveDependency(dependency);
+ }
+
+ protected static Resource toResource(Artifact artifact, SensorContext context) {
+ Project project = Project.createFromMavenIds(artifact.getGroupId(), artifact.getArtifactId());
+ Resource result = context.getResource(project);
+ if (result == null || !((Project) result).getAnalysisVersion().equals(artifact.getBaseVersion())) {
+ Library lib = new Library(project.getKey(), artifact.getBaseVersion());
+ context.saveResource(lib);
+ result = context.getResource(lib);
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Maven dependencies";
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
new file mode 100644
index 00000000000..03a74c830af
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/ProjectDsmDecorator.java
@@ -0,0 +1,106 @@
+/*
+ * 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 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 ProjectDsmDecorator(SonarIndex index) {
+ this.index = index;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ 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;
+ }
+
+ /**
+ * sub-projects, including all descendants but not only direct children
+ */
+ private Collection<Resource> getSubProjects(final Project project) {
+ List<Resource> subProjects = Lists.newArrayList();
+ addSubProjects(project, subProjects);
+ return subProjects;
+ }
+
+ private void addSubProjects(Project project, List<Resource> subProjects) {
+ for (Project subProject : project.getModules()) {
+ Project indexedSubProject = index.getResource(subProject);
+ if (indexedSubProject != null) {
+ subProjects.add(indexedSubProject);
+ }
+ addSubProjects(subProject, subProjects);
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return ResourceUtils.isProject(resource) && !((Project) resource).getModules().isEmpty();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
new file mode 100644
index 00000000000..b0556819c1d
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/SubProjectDsmDecorator.java
@@ -0,0 +1,123 @@
+/*
+ * 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.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 SubProjectDsmDecorator(SonarIndex index) {
+ this.index = index;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ 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));
+ }
+ }
+
+ 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;
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return ResourceUtils.isProject(resource) && ((Project) resource).getModules().isEmpty();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/TangleIndexDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/TangleIndexDecorator.java
new file mode 100644
index 00000000000..c4c1cc3a241
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/TangleIndexDecorator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class TangleIndexDecorator implements Decorator {
+
+ private Metric tanglesMetric;
+ private Metric edgesWeightMetric;
+ private Metric tangleIndexMetric;
+
+ protected TangleIndexDecorator(Metric tanglesMetric, Metric edgesWeightMetric, Metric tangleIndexMetric) {
+ this.tanglesMetric = tanglesMetric;
+ this.edgesWeightMetric = edgesWeightMetric;
+ this.tangleIndexMetric = tangleIndexMetric;
+ }
+
+ @DependsUpon
+ public final List<Metric> dependsUponMetrics() {
+ return Arrays.asList(tanglesMetric, edgesWeightMetric);
+ }
+
+ /**
+ * Used to define downstream dependencies
+ */
+ @DependedUpon
+ public final Metric generatesMetric() {
+ return tangleIndexMetric;
+ }
+
+ public final boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(context)) {
+ return;
+ }
+ Measure tangles = context.getMeasure(tanglesMetric);
+ Measure totalweight = context.getMeasure(edgesWeightMetric);
+
+ if (MeasureUtils.hasValue(totalweight)) {
+ context.saveMeasure(new Measure(tangleIndexMetric, compute(MeasureUtils.getValue(tangles, 0.0), totalweight.getValue())));
+ }
+ }
+
+ private boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(tangleIndexMetric) == null;
+ }
+
+
+ private double compute(double tangles, double totalWeight) {
+ if (totalWeight==0.0) {
+ return 0.0;
+ }
+ double result = 2 * tangles / totalWeight;
+ return result * 100;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
index 17d935699cd..d73f1fcab1a 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
@@ -53,6 +53,7 @@ import org.sonar.batch.ProjectTree;
import org.sonar.batch.issue.DeprecatedViolations;
import org.sonar.batch.issue.ModuleIssues;
import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.batch.scan2.DefaultSensorContext;
import org.sonar.core.component.ComponentKeys;
import org.sonar.core.component.ScanGraph;
@@ -214,6 +215,10 @@ public class DefaultIndex extends SonarIndex {
if (metric == null) {
throw new SonarException("Unknown metric: " + measure.getMetricKey());
}
+ if (DefaultSensorContext.INTERNAL_METRICS.contains(metric)) {
+ LOG.warn("Metric " + metric + " is an internal metric computed by SonarQube. Please update your plugin.");
+ return measure;
+ }
measure.setMetric(metric);
if (measureCache.contains(resource, measure)) {
throw new SonarException("Can not add the same measure twice on " + resource + ": " + measure);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
index e17c5a087d5..001fc1ad72d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
@@ -20,7 +20,6 @@
package org.sonar.batch.mediumtest;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.SonarPlugin;
@@ -235,7 +234,7 @@ public class BatchMediumTester {
private Map<InputFile, SymbolData> symbolTablePerFile = new HashMap<InputFile, SymbolData>();
private Map<String, Map<String, TestCase>> testCasesPerFile = new HashMap<String, Map<String, TestCase>>();
private Map<String, Map<String, Map<String, List<Integer>>>> coveragePerTest = new HashMap<String, Map<String, Map<String, List<Integer>>>>();
- private Map<String, Map<String, String>> dependencies = new HashMap<String, Map<String, String>>();
+ private Map<String, Map<String, Integer>> dependencies = new HashMap<String, Map<String, Integer>>();
@Override
public void scanTaskCompleted(ProjectScanContainer container) {
@@ -322,9 +321,9 @@ public class BatchMediumTester {
String fromKey = entry.key()[1].toString();
String toKey = entry.key()[2].toString();
if (!dependencies.containsKey(fromKey)) {
- dependencies.put(fromKey, new HashMap<String, String>());
+ dependencies.put(fromKey, new HashMap<String, Integer>());
}
- dependencies.get(fromKey).put(toKey, StringUtils.trimToEmpty(entry.value().usage()));
+ dependencies.get(fromKey).put(toKey, entry.value().weight());
}
}
@@ -409,10 +408,10 @@ public class BatchMediumTester {
}
/**
- * @return null if no dependency else return dependency usage.
+ * @return null if no dependency else return dependency weight.
*/
@CheckForNull
- public String dependencyUsage(InputFile from, InputFile to) {
+ public Integer dependencyWeight(InputFile from, InputFile to) {
String fromKey = ((DefaultInputFile) from).key();
String toKey = ((DefaultInputFile) to).key();
return dependencies.containsKey(fromKey) ? dependencies.get(fromKey).get(toKey) : null;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
index 64ef5fb2ddc..ae6b1aa4ee6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
@@ -147,7 +147,7 @@ public class ModuleScanContainer extends ComponentContainer {
TimeMachineConfiguration.class,
DefaultSensorContext.class,
- SensorContextAdaptor.class,
+ SensorContextAdapter.class,
BatchExtensionDictionnary.class,
DefaultTimeMachine.class,
ViolationFilters.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java
index 59375015b11..121a106a9be 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java
@@ -20,7 +20,10 @@
package org.sonar.batch.scan;
import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SonarIndex;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
@@ -67,21 +70,25 @@ import java.util.List;
* Will be dropped once old {@link Sensor} API is dropped.
*
*/
-public class SensorContextAdaptor extends BaseSensorContext {
+public class SensorContextAdapter extends BaseSensorContext {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SensorContextAdapter.class);
private final org.sonar.api.batch.SensorContext sensorContext;
private final MetricFinder metricFinder;
private final Project project;
private final ResourcePerspectives perspectives;
+ private final SonarIndex sonarIndex;
- public SensorContextAdaptor(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives,
+ public SensorContextAdapter(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives,
Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache, BlockCache blockCache,
- DuplicationCache duplicationCache) {
+ DuplicationCache duplicationCache, SonarIndex sonarIndex) {
super(settings, fs, activeRules, componentDataCache, blockCache, duplicationCache);
this.sensorContext = sensorContext;
this.metricFinder = metricFinder;
this.project = project;
this.perspectives = perspectives;
+ this.sonarIndex = sonarIndex;
}
@Override
@@ -139,7 +146,6 @@ public class SensorContextAdaptor extends BaseSensorContext {
if (m == null) {
throw new IllegalStateException("Unknow metric with key: " + measure.metric().key());
}
-
org.sonar.api.measures.Measure measureToSave = new org.sonar.api.measures.Measure(m);
setValueAccordingToMetricType(measure, m, measureToSave);
if (measure.inputFile() != null) {
@@ -297,18 +303,27 @@ public class SensorContextAdaptor extends BaseSensorContext {
}
@Override
- public void saveDependency(InputFile from, InputFile to, String usage) {
+ public void saveDependency(InputFile from, InputFile to, int weight) {
File fromResource = getFile(from);
File toResource = getFile(to);
+ if (sonarIndex.getEdge(fromResource, toResource) != null) {
+ throw new IllegalStateException("Dependency between " + from + " and " + to + " was already saved.");
+ }
Directory fromParent = fromResource.getParent();
Directory toParent = toResource.getParent();
Dependency parentDep = null;
if (!fromParent.equals(toParent)) {
- parentDep = new Dependency(fromParent, toParent).setUsage("USES");
- parentDep = sensorContext.saveDependency(parentDep);
+ parentDep = sonarIndex.getEdge(fromParent, toParent);
+ if (parentDep != null) {
+ parentDep.setWeight(parentDep.getWeight() + 1);
+ } else {
+ parentDep = new Dependency(fromParent, toParent).setUsage("USES").setWeight(1);
+ parentDep = sensorContext.saveDependency(parentDep);
+ }
}
sensorContext.saveDependency(new Dependency(fromResource, toResource)
- .setUsage(usage)
+ .setUsage("USES")
+ .setWeight(weight)
.setParent(parentDep));
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java
index 48a74cccd81..ddbfd0c5d3f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java
@@ -43,7 +43,7 @@ import org.sonar.batch.duplication.DefaultTokenBuilder;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.batch.index.ComponentDataCache;
-import org.sonar.batch.scan.SensorContextAdaptor;
+import org.sonar.batch.scan.SensorContextAdapter;
import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
import org.sonar.duplications.internal.pmd.PmdBlockChunker;
@@ -51,7 +51,7 @@ import java.io.Serializable;
import java.util.List;
/**
- * Common bits between {@link DefaultSensorContext} and {@link SensorContextAdaptor}
+ * Common bits between {@link DefaultSensorContext} and {@link SensorContextAdapter}
* @author julien
*
*/
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
index 1ceee76c6d7..57d362c699d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
@@ -21,6 +21,8 @@ package org.sonar.batch.scan2;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
@@ -35,6 +37,7 @@ import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.batch.sensor.test.TestCase;
import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.MessageException;
import org.sonar.batch.dependency.DependencyCache;
@@ -43,16 +46,31 @@ import org.sonar.batch.duplication.BlockCache;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.issue.IssueFilters;
-import org.sonar.batch.scan.SensorContextAdaptor;
+import org.sonar.batch.scan.SensorContextAdapter;
import org.sonar.batch.test.CoveragePerTestCache;
import org.sonar.batch.test.TestCaseCache;
import org.sonar.core.component.ComponentKeys;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.List;
public class DefaultSensorContext extends BaseSensorContext {
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultSensorContext.class);
+
+ public static final List<Metric> INTERNAL_METRICS = Arrays.<Metric>asList(CoreMetrics.DEPENDENCY_MATRIX,
+ CoreMetrics.DIRECTORY_CYCLES,
+ CoreMetrics.DIRECTORY_EDGES_WEIGHT,
+ CoreMetrics.DIRECTORY_FEEDBACK_EDGES,
+ CoreMetrics.DIRECTORY_TANGLE_INDEX,
+ CoreMetrics.DIRECTORY_TANGLES,
+ CoreMetrics.FILE_CYCLES,
+ CoreMetrics.FILE_EDGES_WEIGHT,
+ CoreMetrics.FILE_FEEDBACK_EDGES,
+ CoreMetrics.FILE_TANGLE_INDEX,
+ CoreMetrics.FILE_TANGLES
+ );
private final AnalyzerMeasureCache measureCache;
private final AnalyzerIssueCache issueCache;
private final ProjectDefinition def;
@@ -98,6 +116,10 @@ public class DefaultSensorContext extends BaseSensorContext {
@Override
public void addMeasure(Measure<?> measure) {
+ if (INTERNAL_METRICS.contains(measure.metric())) {
+ LOG.warn("Metric " + measure.metric() + " is an internal metric computed by SonarQube. Please update your plugin.");
+ return;
+ }
InputFile inputFile = measure.inputFile();
if (inputFile != null) {
measureCache.put(def.getKey(), ComponentKeys.createEffectiveKey(def.getKey(), inputFile), (DefaultMeasure) measure);
@@ -126,7 +148,7 @@ public class DefaultSensorContext extends BaseSensorContext {
updateIssue((DefaultIssue) issue, activeRule);
- if (issueFilters.accept(SensorContextAdaptor.toDefaultIssue(def.getKey(), resourceKey, issue), null)) {
+ if (issueFilters.accept(SensorContextAdapter.toDefaultIssue(def.getKey(), resourceKey, issue), null)) {
issueCache.put(def.getKey(), resourceKey, (DefaultIssue) issue);
return true;
}
@@ -165,10 +187,10 @@ public class DefaultSensorContext extends BaseSensorContext {
}
@Override
- public void saveDependency(InputFile from, InputFile to, String usage) {
+ public void saveDependency(InputFile from, InputFile to, int weight) {
Preconditions.checkNotNull(from);
Preconditions.checkNotNull(to);
- OutgoingDependency dep = new OutgoingDependency(to, usage);
+ OutgoingDependency dep = new OutgoingDependency(to, weight);
dependencyCache.put(def.getKey(), from, dep);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/DsmSerializerTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/DsmSerializerTest.java
new file mode 100644
index 00000000000..24d949d000c
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/DsmSerializerTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.DirectedGraph;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmManualSorter;
+import org.sonar.graph.Edge;
+
+import java.io.IOException;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class DsmSerializerTest {
+ @Test
+ public void serializeEmptyDsm() {
+ Dsm<Resource> dsm = new Dsm<Resource>(new DirectedGraph<Resource, Edge<Resource>>());
+ assertThat(DsmSerializer.serialize(dsm), is("[]"));
+ }
+
+ @Test
+ public void serialize() throws IOException {
+ Resource foo = Directory.create("src/org/foo", "org/foo").setId(7);
+ Resource bar = Directory.create("src/org/bar", "org/bar").setId(8);
+ Dependency dep = new Dependency(foo, bar).setId(30l).setWeight(1);
+
+ DirectedGraph<Resource, Dependency> graph = new DirectedGraph<Resource, Dependency>();
+ graph.addVertex(foo);
+ graph.addVertex(bar);
+ graph.addEdge(dep);
+
+ Dsm<Resource> dsm = new Dsm<Resource>(graph);
+ DsmManualSorter.sort(dsm, bar, foo); // for test reproductibility
+ String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/batch/design/DsmSerializerTest/dsm.json")).trim();
+ assertThat(DsmSerializer.serialize(dsm), is(json));
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/MavenDependenciesSensorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/MavenDependenciesSensorTest.java
new file mode 100644
index 00000000000..504b82981a0
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/MavenDependenciesSensorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.config.Settings;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.Library;
+import org.sonar.api.resources.Project;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MavenDependenciesSensorTest {
+
+ private MavenDependenciesSensor sensor;
+ private Settings settings;
+ private SonarIndex sonarIndex;
+ private SensorContext sensorContext;
+
+ @Before
+ public void prepare() {
+ settings = new Settings();
+ sonarIndex = mock(SonarIndex.class);
+ sensor = new MavenDependenciesSensor(settings, sonarIndex);
+ sensorContext = mock(SensorContext.class);
+ when(sensorContext.getResource(any(Library.class))).thenAnswer(new Answer<Library>() {
+ public Library answer(InvocationOnMock invocation) throws Throwable {
+ return (invocation.getArguments()[0] instanceof Library) ? (Library) invocation.getArguments()[0] : null;
+ }
+ });
+
+ }
+
+ @Test
+ public void testDependenciesProvidedAsProperties() {
+ settings
+ .setProperty(
+ "sonar.maven.projectDependencies",
+ "[{\"k\":\"antlr:antlr\",\"v\":\"2.7.2\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"commons-beanutils:commons-beanutils\",\"v\":\"1.7.0\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"commons-chain:commons-chain\",\"v\":\"1.1\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"commons-digester:commons-digester\",\"v\":\"1.8\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"commons-fileupload:commons-fileupload\",\"v\":\"1.1.1\",\"s\":\"compile\",\"d\":[{\"k\":\"commons-io:commons-io\",\"v\":\"1.1\",\"s\":\"compile\",\"d\":[]}]},"
+ + "{\"k\":\"commons-logging:commons-logging\",\"v\":\"1.0.4\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"commons-validator:commons-validator\",\"v\":\"1.3.1\",\"s\":\"compile\",\"d\":[]},"
+ + "{\"k\":\"javax.servlet:servlet-api\",\"v\":\"2.3\",\"s\":\"provided\",\"d\":[]},"
+ + "{\"k\":\"junit:junit\",\"v\":\"3.8.1\",\"s\":\"test\",\"d\":[]},"
+ + "{\"k\":\"oro:oro\",\"v\":\"2.0.8\",\"s\":\"compile\",\"d\":[]}]");
+
+ Project project = new Project("foo");
+ sensor.analyse(project, sensorContext);
+
+ Library antlr = new Library("antlr:antlr", "2.7.2");
+ verify(sensorContext).saveResource(eq(antlr));
+ Library commonsFU = new Library("commons-fileupload:commons-fileupload", "1.1.1");
+ verify(sensorContext).saveResource(eq(commonsFU));
+ Library commonsIo = new Library("commons-io:commons-io", "1.1");
+ verify(sensorContext).saveResource(eq(commonsIo));
+ Library junit = new Library("junit:junit", "3.8.1");
+ verify(sensorContext).saveResource(eq(junit));
+
+ verify(sensorContext).saveDependency(new Dependency(project, antlr).setUsage("compile").setWeight(1));
+ verify(sensorContext).saveDependency(new Dependency(commonsFU, commonsIo).setUsage("compile").setWeight(1));
+ verify(sensorContext).saveDependency(new Dependency(project, junit).setUsage("test").setWeight(1));
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
index addf1f8bb0c..f6fd70f4a5c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
@@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.SonarIndex;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
@@ -58,7 +59,7 @@ public class SensorContextAdapterTest {
private ActiveRules activeRules;
private DefaultFileSystem fs;
- private SensorContextAdaptor adaptor;
+ private SensorContextAdapter adaptor;
private SensorContext sensorContext;
private Settings settings;
private ResourcePerspectives resourcePerspectives;
@@ -74,8 +75,8 @@ public class SensorContextAdapterTest {
resourcePerspectives = mock(ResourcePerspectives.class);
ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
BlockCache blockCache = mock(BlockCache.class);
- adaptor = new SensorContextAdaptor(sensorContext, metricFinder, new Project("myProject"),
- resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class));
+ adaptor = new SensorContextAdapter(sensorContext, metricFinder, new Project("myProject"),
+ resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class), mock(SonarIndex.class));
}
@Test
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/design/DsmSerializerTest/dsm.json b/sonar-batch/src/test/resources/org/sonar/batch/design/DsmSerializerTest/dsm.json
new file mode 100644
index 00000000000..57996b089e7
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/design/DsmSerializerTest/dsm.json
@@ -0,0 +1 @@
+[{"i":8,"n":"src/org/bar","q":"DIR","v":[{},{"i":30,"w":1}]},{"i":7,"n":"src/org/foo","q":"DIR","v":[{},{}]}]