diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-09-11 23:53:19 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-09-12 09:11:12 +0200 |
commit | d97f7932907682a90f16e6d04b5b11b7af48554b (patch) | |
tree | d3f9b0c65271dd0a223312b0026f78eb59fbcb27 | |
parent | e658e802f50a801641985ef90518fda6e3534716 (diff) | |
download | sonarqube-d97f7932907682a90f16e6d04b5b11b7af48554b.tar.gz sonarqube-d97f7932907682a90f16e6d04b5b11b7af48554b.zip |
SONAR-5389 Add a new dependency API
14 files changed, 236 insertions, 13 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java index 41527929431..33172ca4aaa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java @@ -26,7 +26,6 @@ import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.Event; 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.MeasuresFilter; import org.sonar.api.measures.MeasuresFilters; @@ -156,9 +155,6 @@ public class DefaultDecoratorContext implements DecoratorContext { if (index > -1) { if (metricMeasures.get(index) == measure) { add = false; - } else if (measure.getMetric().equals(CoreMetrics.TESTS)) { - // Hack for SONAR-5212 - measuresByMetric.remove(measure.getMetric().getKey(), metricMeasures.get(index)); } else { throw new SonarException("Can not add twice the same measure on " + resource + ": " + measure); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java new file mode 100644 index 00000000000..18dab70aaa3 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java @@ -0,0 +1,65 @@ +/* + * 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.dependency; + +import com.google.common.base.Preconditions; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.index.Caches; +import org.sonar.batch.scan.filesystem.InputPathCache; + +import javax.annotation.CheckForNull; + +/** + * Cache of all dependencies. This cache is shared amongst all project modules. + * module key -> from key -> to key -> OutgoingDependency + */ +public class DependencyCache implements BatchComponent { + + private final Cache<OutgoingDependency> cache; + + public DependencyCache(Caches caches, InputPathCache inputPathCache) { + cache = caches.createCache("dependencies"); + } + + public Iterable<Entry<OutgoingDependency>> entries() { + return cache.entries(); + } + + @CheckForNull + public OutgoingDependency get(String moduleKey, InputFile from, InputFile to) { + Preconditions.checkNotNull(moduleKey); + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + return cache.get(moduleKey, ((DefaultInputFile) from).key(), ((DefaultInputFile) to).key()); + } + + public DependencyCache put(String moduleKey, InputFile from, OutgoingDependency dependency) { + Preconditions.checkNotNull(moduleKey); + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(dependency); + cache.put(moduleKey, ((DefaultInputFile) from).key(), ((DefaultInputFile) dependency.to()).key(), dependency); + return this; + } + +} 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 new file mode 100644 index 00000000000..b768687b040 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java @@ -0,0 +1,42 @@ +/* + * 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.dependency; + +import org.sonar.api.batch.fs.InputFile; + +public class OutgoingDependency { + + private final InputFile to; + private final String usage; + + public OutgoingDependency(InputFile to, String usage) { + this.to = to; + this.usage = usage; + } + + public InputFile to() { + return to; + } + + public String usage() { + return usage; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/package-info.java new file mode 100644 index 00000000000..56e93fec2f3 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.dependency; + +import javax.annotation.ParametersAreNonnullByDefault; + 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 088a0fbda43..17d935699cd 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 @@ -31,7 +31,6 @@ import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.MeasuresFilters; @@ -216,9 +215,7 @@ public class DefaultIndex extends SonarIndex { throw new SonarException("Unknown metric: " + measure.getMetricKey()); } measure.setMetric(metric); - if (measureCache.contains(resource, measure) - // Hack for SONAR-5212 - && !measure.getMetric().equals(CoreMetrics.TESTS)) { + if (measureCache.contains(resource, measure)) { throw new SonarException("Can not add the same measure twice on " + resource + ": " + measure); } measureCache.put(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 16f058187a0..e17c5a087d5 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,6 +20,7 @@ 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; @@ -42,6 +43,8 @@ import org.sonar.batch.bootstrap.PluginsReferential; import org.sonar.batch.bootstrap.TaskProperties; import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.batch.dependency.DependencyCache; +import org.sonar.batch.dependency.OutgoingDependency; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.highlighting.SyntaxHighlightingData; import org.sonar.batch.highlighting.SyntaxHighlightingRule; @@ -232,6 +235,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>>(); @Override public void scanTaskCompleted(ProjectScanContainer container) { @@ -249,6 +253,8 @@ public class BatchMediumTester { storeDuplication(container); storeTestCases(container); storeCoveragePerTest(container); + storeDependencies(container); + } private void storeCoveragePerTest(ProjectScanContainer container) { @@ -310,6 +316,18 @@ public class BatchMediumTester { } } + private void storeDependencies(ProjectScanContainer container) { + DependencyCache dependencyCache = container.getComponentByType(DependencyCache.class); + for (Entry<OutgoingDependency> entry : dependencyCache.entries()) { + String fromKey = entry.key()[1].toString(); + String toKey = entry.key()[2].toString(); + if (!dependencies.containsKey(fromKey)) { + dependencies.put(fromKey, new HashMap<String, String>()); + } + dependencies.get(fromKey).put(toKey, StringUtils.trimToEmpty(entry.value().usage())); + } + } + public List<Issue> issues() { return issues; } @@ -389,6 +407,16 @@ public class BatchMediumTester { } return null; } + + /** + * @return null if no dependency else return dependency usage. + */ + @CheckForNull + public String dependencyUsage(InputFile from, InputFile to) { + String fromKey = ((DefaultInputFile) from).key(); + String toKey = ((DefaultInputFile) to).key(); + return dependencies.containsKey(fromKey) ? dependencies.get(fromKey).get(toKey) : null; + } } private static class FakeGlobalReferentialsLoader implements GlobalReferentialsLoader { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java index 37fe0b134bf..59375015b11 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java @@ -35,6 +35,7 @@ import org.sonar.api.batch.sensor.test.TestCase; import org.sonar.api.batch.sensor.test.internal.DefaultTestCase; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; +import org.sonar.api.design.Dependency; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.measures.Formula; @@ -56,6 +57,7 @@ import org.sonar.batch.duplication.BlockCache; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.scan2.BaseSensorContext; +import org.sonar.core.component.ComponentKeys; import java.io.Serializable; import java.util.List; @@ -204,7 +206,7 @@ public class SensorContextAdaptor extends BaseSensorContext { if (issuable == null) { return false; } - return issuable.addIssue(toDefaultIssue(project.getKey(), r.getKey(), issue)); + return issuable.addIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); } public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) { @@ -286,4 +288,28 @@ public class SensorContextAdaptor extends BaseSensorContext { return mainRes; } + private File getFile(InputFile file) { + if (file.type() == InputFile.Type.MAIN) { + return getMainResource(file); + } else { + return getTestResource(file); + } + } + + @Override + public void saveDependency(InputFile from, InputFile to, String usage) { + File fromResource = getFile(from); + File toResource = getFile(to); + 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); + } + sensorContext.saveDependency(new Dependency(fromResource, toResource) + .setUsage(usage) + .setParent(parentDep)); + } + } 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 e995b805490..1ceee76c6d7 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 @@ -37,6 +37,8 @@ import org.sonar.api.batch.sensor.test.internal.DefaultTestCase; import org.sonar.api.config.Settings; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.MessageException; +import org.sonar.batch.dependency.DependencyCache; +import org.sonar.batch.dependency.OutgoingDependency; import org.sonar.batch.duplication.BlockCache; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.ComponentDataCache; @@ -58,10 +60,11 @@ public class DefaultSensorContext extends BaseSensorContext { private final IssueFilters issueFilters; private final TestCaseCache testCaseCache; private final CoveragePerTestCache coveragePerTestCache; + private final DependencyCache dependencyCache; public DefaultSensorContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache, Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache, - BlockCache blockCache, DuplicationCache duplicationCache, TestCaseCache testCaseCache, CoveragePerTestCache coveragePerTestCache) { + BlockCache blockCache, DuplicationCache duplicationCache, TestCaseCache testCaseCache, CoveragePerTestCache coveragePerTestCache, DependencyCache dependencyCache) { super(settings, fs, activeRules, componentDataCache, blockCache, duplicationCache); this.def = def; this.measureCache = measureCache; @@ -70,6 +73,7 @@ public class DefaultSensorContext extends BaseSensorContext { this.issueFilters = issueFilters; this.testCaseCache = testCaseCache; this.coveragePerTestCache = coveragePerTestCache; + this.dependencyCache = dependencyCache; } @Override @@ -160,4 +164,12 @@ public class DefaultSensorContext extends BaseSensorContext { coveragePerTestCache.put(testCase, coveredFile, coveredLines); } + @Override + public void saveDependency(InputFile from, InputFile to, String usage) { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + OutgoingDependency dep = new OutgoingDependency(to, usage); + dependencyCache.put(def.getKey(), from, dep); + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java index 5922b613ffb..d5d7ffa6a99 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java @@ -33,6 +33,7 @@ import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; +import org.sonar.batch.dependency.DependencyCache; import org.sonar.batch.duplication.BlockCache; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.Caches; @@ -120,6 +121,9 @@ public class ProjectScanContainer extends ComponentContainer { TestCaseCache.class, CoveragePerTestCache.class, + // Dependencies + DependencyCache.class, + ScanTaskObservers.class); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java index adf80cf8cf8..ea96a8678b1 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java @@ -170,7 +170,9 @@ public interface SensorContext { * * @param force allows to force creation of violation even if it was supressed by {@link org.sonar.api.rules.ViolationFilter} * @since 2.5 + * @deprecated since 5.0 but force parameter was ignored for a long time anyway */ + @Deprecated void saveViolation(Violation violation, boolean force); /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java index 32ea0e3cf2e..357a7c99103 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java @@ -90,4 +90,5 @@ public interface InputFile extends InputPath { * zero if the file is empty. */ int lines(); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java index f2102986b38..1729795b06d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java @@ -190,4 +190,13 @@ public interface SensorContext { */ void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines); + // ------------ DEPENDENCIES ------------ + + /** + * Declare a dependency between 2 files. + * @param usage A qualifier for the dependency. + * @since 5.0 + */ + void saveDependency(InputFile from, InputFile to, String usage); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/NoSonarFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/NoSonarFilter.java index 9331dcac10f..591aa0f539b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/NoSonarFilter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/NoSonarFilter.java @@ -19,10 +19,11 @@ */ package org.sonar.api.issue; -import org.sonar.api.issue.batch.IssueFilterChain; - import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.issue.batch.IssueFilterChain; import java.util.Map; import java.util.Set; @@ -40,11 +41,26 @@ public class NoSonarFilter implements org.sonar.api.issue.batch.IssueFilter { private final Map<String, Set<Integer>> noSonarLinesByResource = Maps.newHashMap(); + /** + * @deprecated since 5.0 use {@link #noSonarInFile(InputFile, Set)} + */ + @Deprecated public NoSonarFilter addComponent(String componentKey, Set<Integer> noSonarLines) { noSonarLinesByResource.put(componentKey, noSonarLines); return this; } + /** + * Register lines in a file that contains the NOSONAR flag. + * @param inputFile + * @param noSonarLines Line number starts at 1 in a file + * @since 5.0 + */ + public NoSonarFilter noSonarInFile(InputFile inputFile, Set<Integer> noSonarLines) { + noSonarLinesByResource.put(((DefaultInputFile) inputFile).key(), noSonarLines); + return this; + } + @Override public boolean accept(Issue issue, IssueFilterChain chain) { boolean accepted = true; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java index 50915f36312..d8f49ceb201 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/InputFileUtils.java @@ -20,7 +20,6 @@ package org.sonar.api.resources; import com.google.common.base.Objects; - import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; @@ -34,7 +33,9 @@ import java.util.List; /** * @since 2.8 + * @deprecated since 5.0 as {@link InputFile} is deprecated */ +@Deprecated public final class InputFileUtils { private InputFileUtils() { |