From 6882924ef2d57618e9391c20b1a7a54546a7d9dd Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Mon, 13 Oct 2014 11:54:50 +0200 Subject: [PATCH] SONAR-5389 Refactor dependency API --- .../plugins/cpd/medium/CpdMediumTest.java | 2 +- .../main/java/org/sonar/xoo/XooPlugin.java | 20 ++- .../org/sonar/xoo/lang/DependencySensor.java | 103 ++++++++++++++ .../java/org/sonar/xoo/XooPluginTest.java | 2 +- .../sonar/xoo/lang/DependencySensorTest.java | 107 ++++++++++++++ .../DefaultDependencyValueCoder.java | 68 +++++++++ .../batch/dependency/DependencyCache.java | 19 +-- .../batch/mediumtest/BatchMediumTester.java | 32 +++-- .../batch/scan/SensorContextAdapter.java | 15 +- .../sonar/batch/scan2/BaseSensorContext.java | 7 + .../batch/scan2/DefaultSensorContext.java | 12 +- .../dependency/DependencyMediumTest.java | 95 +++++++++++++ .../mediumtest/fs/FileSystemMediumTest.java | 8 +- .../highlighting/HighlightingMediumTest.java | 7 +- .../mediumtest/symbol/SymbolMediumTest.java | 2 +- .../batch/scan/SensorContextAdapterTest.java | 108 ++++++++++++++- .../sonar/api/batch/sensor/SensorContext.java | 9 +- .../sonar/api/batch/sensor/SensorStorage.java | 3 + .../batch/sensor/dependency/Dependency.java | 54 ++++++++ .../internal/DefaultDependency.java | 131 ++++++++++++++++++ .../dependency/internal/package-info.java | 25 +--- .../batch/sensor/dependency/package-info.java | 21 +++ 22 files changed, 776 insertions(+), 74 deletions(-) create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/DependencySensor.java create mode 100644 plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/DependencySensorTest.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java rename sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java => sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/package-info.java (70%) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/package-info.java diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java index 1344179bfb2..56805e5dd7a 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java @@ -105,7 +105,7 @@ public class CpdMediumTest { // 4 measures per file assertThat(result.measures()).hasSize(8); - InputFile inputFile = result.inputFiles().get(0); + InputFile inputFile = result.inputFile("src/sample1.xoo"); // One clone group List duplicationGroups = result.duplicationsFor(inputFile); assertThat(duplicationGroups).hasSize(1); diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index d28dc487dec..102aa38dfb1 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -20,8 +20,21 @@ package org.sonar.xoo; import org.sonar.api.SonarPlugin; -import org.sonar.xoo.lang.*; -import org.sonar.xoo.rule.*; +import org.sonar.xoo.lang.CoveragePerTestSensor; +import org.sonar.xoo.lang.DependencySensor; +import org.sonar.xoo.lang.MeasureSensor; +import org.sonar.xoo.lang.SymbolReferencesSensor; +import org.sonar.xoo.lang.SyntaxHighlightingSensor; +import org.sonar.xoo.lang.TestCaseSensor; +import org.sonar.xoo.lang.XooTokenizerSensor; +import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; +import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; +import org.sonar.xoo.rule.OneIssuePerLineSensor; +import org.sonar.xoo.rule.XooFakeExporter; +import org.sonar.xoo.rule.XooFakeImporter; +import org.sonar.xoo.rule.XooFakeImporterWithMessages; +import org.sonar.xoo.rule.XooQualityProfile; +import org.sonar.xoo.rule.XooRulesDefinition; import org.sonar.xoo.scm.XooBlameCommand; import org.sonar.xoo.scm.XooScmProvider; @@ -58,11 +71,12 @@ public class XooPlugin extends SonarPlugin { XooTokenizerSensor.class, TestCaseSensor.class, CoveragePerTestSensor.class, + DependencySensor.class, OneIssuePerLineSensor.class, OneIssueOnDirPerFileSensor.class, CreateIssueByInternalKeySensor.class - ); + ); } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/DependencySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/DependencySensor.java new file mode 100644 index 00000000000..929092da866 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/DependencySensor.java @@ -0,0 +1,103 @@ +/* + * 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.xoo.lang; + +import com.google.common.base.Splitter; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.xoo.Xoo; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +/** + * Parse files *.xoo.deps + */ +public class DependencySensor implements Sensor { + + private static final Logger LOG = LoggerFactory.getLogger(DependencySensor.class); + + private static final String DEPS_EXTENSION = ".deps"; + + private void processDependencies(InputFile inputFile, SensorContext context) { + File ioFile = inputFile.file(); + File depsFile = new File(ioFile.getParentFile(), ioFile.getName() + DEPS_EXTENSION); + if (depsFile.exists()) { + LOG.debug("Processing " + depsFile.getAbsolutePath()); + try { + List lines = FileUtils.readLines(depsFile, context.fileSystem().encoding().name()); + int lineNumber = 0; + for (String line : lines) { + lineNumber++; + if (StringUtils.isBlank(line) || line.startsWith("#")) { + continue; + } + processLine(depsFile, lineNumber, context, line, inputFile); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + private void processLine(File coverPerTest, int lineNumber, SensorContext context, String line, InputFile file) { + try { + Iterator split = Splitter.on(":").split(line).iterator(); + String otherFileRelativePath = split.next(); + FileSystem fs = context.fileSystem(); + InputFile otherFile = fs.inputFile(fs.predicates().hasRelativePath(otherFileRelativePath)); + int weight = Integer.parseInt(split.next()); + context.newDependency() + .from(file) + .to(otherFile) + .weight(weight) + .save(); + } catch (Exception e) { + throw new IllegalStateException("Error processing line " + lineNumber + " of file " + coverPerTest.getAbsolutePath(), e); + } + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .name("Xoo Dependency Sensor") + .workOnLanguages(Xoo.KEY) + .workOnFileTypes(InputFile.Type.MAIN); + } + + @Override + public void execute(SensorContext context) { + FileSystem fs = context.fileSystem(); + FilePredicates p = fs.predicates(); + for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(InputFile.Type.MAIN)))) { + processDependencies(file, context); + } + } +} diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java index 193f3b95349..d5913f277a8 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java @@ -27,6 +27,6 @@ public class XooPluginTest { @Test public void provide_extensions() { - assertThat(new XooPlugin().getExtensions()).hasSize(17); + assertThat(new XooPlugin().getExtensions()).hasSize(18); } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/DependencySensorTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/DependencySensorTest.java new file mode 100644 index 00000000000..11ed815ac3c --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/DependencySensorTest.java @@ -0,0 +1,107 @@ +/* + * 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.xoo.lang; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorStorage; +import org.sonar.api.batch.sensor.dependency.Dependency; +import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; + +import java.io.File; +import java.io.IOException; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DependencySensorTest { + + private DependencySensor sensor; + private SensorContext context = mock(SensorContext.class); + private DefaultFileSystem fileSystem; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + private File baseDir; + + @Before + public void prepare() throws IOException { + baseDir = temp.newFolder(); + sensor = new DependencySensor(); + fileSystem = new DefaultFileSystem(); + when(context.fileSystem()).thenReturn(fileSystem); + } + + @Test + public void testDescriptor() { + sensor.describe(new DefaultSensorDescriptor()); + } + + @Test + public void testNoExecutionIfNoDepsFile() { + DefaultInputFile file = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()).setLanguage("xoo") + .setType(Type.MAIN); + fileSystem.add(file); + sensor.execute(context); + } + + @Test + public void testExecution() throws IOException { + File deps = new File(baseDir, "src/foo.xoo.deps"); + FileUtils.write(deps, "src/foo2.xoo:2\nsrc2/foo3.xoo:6\n\n#comment"); + DefaultInputFile inputFile1 = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath()).setLanguage("xoo"); + DefaultInputFile inputFile2 = new DefaultInputFile("foo", "src/foo2.xoo").setAbsolutePath(new File(baseDir, "src/foo2.xoo").getAbsolutePath()).setLanguage("xoo"); + DefaultInputFile inputFile3 = new DefaultInputFile("foo", "src2/foo3.xoo").setAbsolutePath(new File(baseDir, "src2/foo3.xoo").getAbsolutePath()).setLanguage("xoo"); + fileSystem.add(inputFile1); + fileSystem.add(inputFile2); + fileSystem.add(inputFile3); + + final SensorStorage sensorStorage = mock(SensorStorage.class); + + when(context.newDependency()).thenAnswer(new Answer() { + @Override + public Dependency answer(InvocationOnMock invocation) throws Throwable { + return new DefaultDependency(sensorStorage); + } + }); + + sensor.execute(context); + + verify(sensorStorage).store(new DefaultDependency() + .from(inputFile1) + .to(inputFile2) + .weight(2)); + verify(sensorStorage).store(new DefaultDependency() + .from(inputFile1) + .to(inputFile3) + .weight(6)); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java new file mode 100644 index 00000000000..8f01f2c233b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java @@ -0,0 +1,68 @@ +/* + * 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.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; +import org.sonar.batch.scan.filesystem.InputPathCache; + +class DefaultDependencyValueCoder implements ValueCoder { + + private InputPathCache inputPathCache; + + public DefaultDependencyValueCoder(InputPathCache inputPathCache) { + this.inputPathCache = inputPathCache; + } + + @Override + public void put(Value value, Object object, CoderContext context) { + DefaultDependency dep = (DefaultDependency) object; + value.putUTF(((DefaultInputFile) dep.from()).moduleKey()); + value.putUTF(((DefaultInputFile) dep.from()).relativePath()); + value.putUTF(((DefaultInputFile) dep.to()).moduleKey()); + value.putUTF(((DefaultInputFile) dep.to()).relativePath()); + value.put(dep.weight()); + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + String fromModuleKey = value.getString(); + String fromRelativePath = value.getString(); + InputFile from = inputPathCache.getFile(fromModuleKey, fromRelativePath); + if (from == null) { + throw new IllegalStateException("Unable to load InputFile " + fromModuleKey + ":" + fromRelativePath); + } + String toModuleKey = value.getString(); + String toRelativePath = value.getString(); + InputFile to = inputPathCache.getFile(toModuleKey, toRelativePath); + if (to == null) { + throw new IllegalStateException("Unable to load InputFile " + toModuleKey + ":" + toRelativePath); + } + int weight = value.getInt(); + return new DefaultDependency() + .from(from) + .to(to) + .weight(weight); + } +} 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 index 7a064d520fc..9e3709a5532 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java @@ -23,41 +23,44 @@ 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.api.batch.sensor.dependency.Dependency; +import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; 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 + * module key -> from key -> to key -> Dependency */ public class DependencyCache implements BatchComponent { - private final Cache cache; + private final Cache cache; - public DependencyCache(Caches caches) { + public DependencyCache(Caches caches, InputPathCache inputPathCache) { + caches.registerValueCoder(DefaultDependency.class, new DefaultDependencyValueCoder(inputPathCache)); cache = caches.createCache("dependencies"); } - public Iterable> entries() { + public Iterable> entries() { return cache.entries(); } @CheckForNull - public OutgoingDependency get(String moduleKey, InputFile from, InputFile to) { + public Dependency 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) { + public DependencyCache put(String moduleKey, Dependency dependency) { Preconditions.checkNotNull(moduleKey); - Preconditions.checkNotNull(from); Preconditions.checkNotNull(dependency); - cache.put(moduleKey, ((DefaultInputFile) from).key(), ((DefaultInputFile) dependency.to()).key(), dependency); + cache.put(moduleKey, ((DefaultInputFile) dependency.from()).key(), ((DefaultInputFile) dependency.to()).key(), dependency); return this; } 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 61a6a062867..596edcdec5f 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 @@ -29,6 +29,7 @@ import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.dependency.Dependency; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.batch.sensor.issue.Issue; @@ -44,7 +45,6 @@ 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; @@ -229,8 +229,8 @@ public class BatchMediumTester { private List issues = new ArrayList(); private List measures = new ArrayList(); private Map> duplications = new HashMap>(); - private List inputFiles = new ArrayList(); - private List inputDirs = new ArrayList(); + private Map inputFiles = new HashMap(); + private Map inputDirs = new HashMap(); private Map highlightingPerFile = new HashMap(); private Map symbolTablePerFile = new HashMap(); private Map> testCasesPerFile = new HashMap>(); @@ -293,7 +293,7 @@ public class BatchMediumTester { private void storeComponentData(ProjectScanContainer container) { ComponentDataCache componentDataCache = container.getComponentByType(ComponentDataCache.class); - for (InputFile file : inputFiles) { + for (InputFile file : inputFiles.values()) { SyntaxHighlightingData highlighting = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING); if (highlighting != null) { highlightingPerFile.put(file, highlighting); @@ -309,16 +309,16 @@ public class BatchMediumTester { InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class); for (InputPath inputPath : inputFileCache.all()) { if (inputPath instanceof InputFile) { - inputFiles.add((InputFile) inputPath); + inputFiles.put(inputPath.relativePath(), (InputFile) inputPath); } else { - inputDirs.add((InputDir) inputPath); + inputDirs.put(inputPath.relativePath(), (InputDir) inputPath); } } } private void storeDependencies(ProjectScanContainer container) { DependencyCache dependencyCache = container.getComponentByType(DependencyCache.class); - for (Entry entry : dependencyCache.entries()) { + for (Entry entry : dependencyCache.entries()) { String fromKey = entry.key()[1].toString(); String toKey = entry.key()[2].toString(); if (!dependencies.containsKey(fromKey)) { @@ -336,12 +336,22 @@ public class BatchMediumTester { return measures; } - public List inputFiles() { - return inputFiles; + public Collection inputFiles() { + return inputFiles.values(); } - public List inputDirs() { - return inputDirs; + @CheckForNull + public InputFile inputFile(String relativePath) { + return inputFiles.get(relativePath); + } + + public Collection inputDirs() { + return inputDirs.values(); + } + + @CheckForNull + public InputDir inputDir(String relativePath) { + return inputDirs.get(relativePath); } public List duplicationsFor(InputFile inputFile) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java index 90c00db0ef5..e482d8b86e8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java @@ -70,6 +70,7 @@ import java.util.List; */ public class SensorContextAdapter extends BaseSensorContext { + private static final String USES = "USES"; private final org.sonar.api.batch.SensorContext sensorContext; private final MetricFinder metricFinder; private final Project project; @@ -241,11 +242,11 @@ public class SensorContextAdapter extends BaseSensorContext { } @Override - public void saveDependency(InputFile from, InputFile to, int weight) { - File fromResource = getFile(from); - File toResource = getFile(to); + public void store(org.sonar.api.batch.sensor.dependency.Dependency dep) { + File fromResource = getFile(dep.from()); + File toResource = getFile(dep.to()); if (sonarIndex.getEdge(fromResource, toResource) != null) { - throw new IllegalStateException("Dependency between " + from + " and " + to + " was already saved."); + throw new IllegalStateException("Dependency between " + dep.from() + " and " + dep.to() + " was already saved."); } Directory fromParent = fromResource.getParent(); Directory toParent = toResource.getParent(); @@ -255,13 +256,13 @@ public class SensorContextAdapter extends BaseSensorContext { if (parentDep != null) { parentDep.setWeight(parentDep.getWeight() + 1); } else { - parentDep = new Dependency(fromParent, toParent).setUsage("USES").setWeight(1); + parentDep = new Dependency(fromParent, toParent).setUsage(USES).setWeight(1); parentDep = sensorContext.saveDependency(parentDep); } } sensorContext.saveDependency(new Dependency(fromResource, toResource) - .setUsage("USES") - .setWeight(weight) + .setUsage(USES) + .setWeight(dep.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 beb62d97251..bb81865c4b6 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 @@ -26,6 +26,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorStorage; +import org.sonar.api.batch.sensor.dependency.Dependency; +import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder; @@ -155,4 +157,9 @@ public abstract class BaseSensorContext implements SensorContext, SensorStorage return new DefaultTestCase(this); } + @Override + public Dependency newDependency() { + return new DefaultDependency(this); + } + } 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 dff662a8f9e..90fc49ca395 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 @@ -31,6 +31,7 @@ import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.DefaultActiveRule; +import org.sonar.api.batch.sensor.dependency.Dependency; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.batch.sensor.measure.Measure; @@ -42,7 +43,6 @@ import org.sonar.api.measures.CoreMetrics; 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; @@ -172,11 +172,11 @@ public class DefaultSensorContext extends BaseSensorContext { } @Override - public void saveDependency(InputFile from, InputFile to, int weight) { - Preconditions.checkNotNull(from); - Preconditions.checkNotNull(to); - OutgoingDependency dep = new OutgoingDependency(to, weight); - dependencyCache.put(def.getKey(), from, dep); + public void store(Dependency dep) { + if (dependencyCache.get(def.getKey(), dep.from(), dep.to()) != null) { + throw new IllegalStateException("Dependency between " + dep.from() + " and " + dep.to() + " was already saved."); + } + dependencyCache.put(def.getKey(), dep); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java new file mode 100644 index 00000000000..a6fe43a1b5c --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java @@ -0,0 +1,95 @@ +/* + * 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.mediumtest.dependency; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; + +import static org.fest.assertions.Assertions.assertThat; + +public class DependencyMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public TestName testName = new TestName(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void populateDependenciesOnTempProject() throws IOException { + + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + File xooFile2 = new File(srcDir, "sample2.xoo"); + File xooFile3 = new File(srcDir, "foo/sample3.xoo"); + File xooDepsFile = new File(srcDir, "sample.xoo.deps"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + FileUtils.write(xooFile2, "Sample xoo\ncontent"); + FileUtils.write(xooFile3, "Sample xoo\ncontent"); + FileUtils.write(xooDepsFile, "src/sample2.xoo:3\nsrc/foo/sample3.xoo:6"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/sample2.xoo"))).isEqualTo(3); + assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/foo/sample3.xoo"))).isEqualTo(6); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java index 19d7aea020a..08ac9d7e3cd 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java @@ -94,9 +94,9 @@ public class FileSystemMediumTest { assertThat(result.inputFiles()).hasSize(1); assertThat(result.inputDirs()).hasSize(1); - assertThat(result.inputFiles().get(0).type()).isEqualTo(InputFile.Type.MAIN); - assertThat(result.inputFiles().get(0).relativePath()).isEqualTo("src/sample.xoo"); - assertThat(result.inputDirs().get(0).relativePath()).isEqualTo("src"); + assertThat(result.inputFile("src/sample.xoo").type()).isEqualTo(InputFile.Type.MAIN); + assertThat(result.inputFile("src/sample.xoo").relativePath()).isEqualTo("src/sample.xoo"); + assertThat(result.inputDir("src").relativePath()).isEqualTo("src"); } @Test @@ -115,7 +115,7 @@ public class FileSystemMediumTest { .start(); assertThat(result.inputFiles()).hasSize(1); - assertThat(result.inputFiles().get(0).type()).isEqualTo(InputFile.Type.TEST); + assertThat(result.inputFile("test/sampleTest.xoo").type()).isEqualTo(InputFile.Type.TEST); } /** diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java index b6ce7fb7d52..93ae29ce062 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java @@ -19,8 +19,6 @@ */ package org.sonar.batch.mediumtest.highlighting; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; - import com.google.common.collect.ImmutableMap; import org.apache.commons.io.FileUtils; import org.junit.After; @@ -30,6 +28,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestName; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; import org.sonar.xoo.XooPlugin; @@ -87,7 +86,7 @@ public class HighlightingMediumTest { .build()) .start(); - InputFile file = result.inputFiles().get(0); + InputFile file = result.inputFile("src/sample.xoo"); assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING); assertThat(result.highlightingTypeFor(file, 9)).containsExactly(TypeOfText.STRING); assertThat(result.highlightingTypeFor(file, 10)).isEmpty(); @@ -126,7 +125,7 @@ public class HighlightingMediumTest { .start(); System.out.println("Duration: " + (System.currentTimeMillis() - start)); - InputFile file = result.inputFiles().get(0); + InputFile file = result.inputFile("src/sample.xoo"); assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java index 8772d371ba2..10c9f37f47f 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java @@ -80,7 +80,7 @@ public class SymbolMediumTest { .build()) .start(); - InputFile file = result.inputFiles().get(0); + InputFile file = result.inputFile("src/sample.xoo"); assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(7, 27); } 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 f949f7c544d..df477befe80 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 @@ -28,16 +28,19 @@ import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; 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.Issue; import org.sonar.api.measures.CoreMetrics; @@ -55,6 +58,8 @@ import org.sonar.batch.index.ComponentDataCache; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class SensorContextAdapterTest { @@ -69,6 +74,7 @@ public class SensorContextAdapterTest { private Settings settings; private ResourcePerspectives resourcePerspectives; private Project project; + private SonarIndex sonarIndex; @Before public void prepare() { @@ -83,8 +89,9 @@ public class SensorContextAdapterTest { ComponentDataCache componentDataCache = mock(ComponentDataCache.class); BlockCache blockCache = mock(BlockCache.class); project = new Project("myProject"); + sonarIndex = mock(SonarIndex.class); adaptor = new SensorContextAdapter(sensorContext, metricFinder, project, - resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class), mock(SonarIndex.class)); + resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class), sonarIndex); } @Test @@ -239,4 +246,103 @@ public class SensorContextAdapterTest { assertThat(issue.effortToFix()).isEqualTo(10.0); } + @Test + public void shouldStoreDependencyInSameFolder() { + + File foo = File.create("src/Foo.java"); + File bar = File.create("src/Bar.java"); + when(sensorContext.getResource(foo)).thenReturn(foo); + when(sensorContext.getResource(bar)).thenReturn(bar); + + adaptor.store(new DefaultDependency() + .from(new DefaultInputFile("foo", "src/Foo.java").setType(Type.MAIN)) + .to(new DefaultInputFile("foo", "src/Bar.java").setType(Type.MAIN)) + .weight(3)); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Dependency.class); + + verify(sensorContext).saveDependency(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().getFrom()).isEqualTo(foo); + assertThat(argumentCaptor.getValue().getTo()).isEqualTo(bar); + assertThat(argumentCaptor.getValue().getWeight()).isEqualTo(3); + assertThat(argumentCaptor.getValue().getUsage()).isEqualTo("USES"); + } + + @Test + public void throw_if_attempt_to_save_same_dep_twice() { + + File foo = File.create("src/Foo.java"); + File bar = File.create("src/Bar.java"); + when(sensorContext.getResource(foo)).thenReturn(foo); + when(sensorContext.getResource(bar)).thenReturn(bar); + when(sonarIndex.getEdge(foo, bar)).thenReturn(new Dependency(foo, bar)); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Dependency between [moduleKey=foo, relative=src/Foo.java, abs=null] and [moduleKey=foo, relative=src/Bar.java, abs=null] was already saved."); + + adaptor.store(new DefaultDependency() + .from(new DefaultInputFile("foo", "src/Foo.java").setType(Type.MAIN)) + .to(new DefaultInputFile("foo", "src/Bar.java").setType(Type.MAIN)) + .weight(3)); + } + + @Test + public void shouldStoreDependencyInDifferentFolder() { + + File foo = File.create("src1/Foo.java"); + File bar = File.create("src2/Bar.java"); + when(sensorContext.getResource(foo)).thenReturn(foo); + when(sensorContext.getResource(bar)).thenReturn(bar); + + adaptor.store(new DefaultDependency() + .from(new DefaultInputFile("foo", "src1/Foo.java").setType(Type.MAIN)) + .to(new DefaultInputFile("foo", "src2/Bar.java").setType(Type.MAIN)) + .weight(3)); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Dependency.class); + + verify(sensorContext, times(2)).saveDependency(argumentCaptor.capture()); + assertThat(argumentCaptor.getAllValues()).hasSize(2); + Dependency value1 = argumentCaptor.getAllValues().get(0); + assertThat(value1.getFrom()).isEqualTo(Directory.create("src1")); + assertThat(value1.getTo()).isEqualTo(Directory.create("src2")); + assertThat(value1.getWeight()).isEqualTo(1); + assertThat(value1.getUsage()).isEqualTo("USES"); + + Dependency value2 = argumentCaptor.getAllValues().get(1); + assertThat(value2.getFrom()).isEqualTo(foo); + assertThat(value2.getTo()).isEqualTo(bar); + assertThat(value2.getWeight()).isEqualTo(3); + assertThat(value2.getUsage()).isEqualTo("USES"); + } + + @Test + public void shouldIncrementParentWeight() { + + File foo = File.create("src1/Foo.java"); + File bar = File.create("src2/Bar.java"); + Directory src1 = Directory.create("src1"); + Directory src2 = Directory.create("src2"); + when(sensorContext.getResource(foo)).thenReturn(foo); + when(sensorContext.getResource(bar)).thenReturn(bar); + Dependency parentDep = new Dependency(src1, src2).setWeight(4); + when(sonarIndex.getEdge(src1, src2)).thenReturn(parentDep); + + adaptor.store(new DefaultDependency() + .from(new DefaultInputFile("foo", "src1/Foo.java").setType(Type.MAIN)) + .to(new DefaultInputFile("foo", "src2/Bar.java").setType(Type.MAIN)) + .weight(3)); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Dependency.class); + + verify(sensorContext).saveDependency(argumentCaptor.capture()); + + assertThat(parentDep.getWeight()).isEqualTo(5); + + Dependency value = argumentCaptor.getValue(); + assertThat(value.getFrom()).isEqualTo(foo); + assertThat(value.getTo()).isEqualTo(bar); + assertThat(value.getWeight()).isEqualTo(3); + assertThat(value.getUsage()).isEqualTo("USES"); + } } 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 76cab2f4790..831ea878114 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 @@ -22,6 +22,7 @@ package org.sonar.api.batch.sensor; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.sensor.dependency.Dependency; import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder; @@ -111,7 +112,7 @@ public interface SensorContext { // ------------ TESTS ------------ /** - * Create a new test case for the given test file. + * Create a new test case. * Don't forget to call {@link TestCase#save()} once all parameters are provided. * @since 5.0 */ @@ -130,10 +131,10 @@ public interface SensorContext { // ------------ DEPENDENCIES ------------ /** - * Declare a dependency between 2 files. - * @param weight Weight of the dependency + * Create a new dependency. + * Don't forget to call {@link Dependency#save()} once all parameters are provided. * @since 5.0 */ - void saveDependency(InputFile from, InputFile to, int weight); + Dependency newDependency(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorStorage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorStorage.java index 8298250523b..7faec27f4c6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorStorage.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorStorage.java @@ -19,6 +19,7 @@ */ package org.sonar.api.batch.sensor; +import org.sonar.api.batch.sensor.dependency.Dependency; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.test.TestCase; @@ -35,4 +36,6 @@ public interface SensorStorage { void store(TestCase testCase); + void store(Dependency dependency); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java new file mode 100644 index 00000000000..87bd3029fbd --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java @@ -0,0 +1,54 @@ +/* + * 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.api.batch.sensor.dependency; + +import com.google.common.annotations.Beta; +import org.sonar.api.batch.fs.InputFile; + +/** + * @since 5.0 + */ +@Beta +public interface Dependency { + + InputFile from(); + + Dependency from(InputFile from); + + InputFile to(); + + Dependency to(InputFile to); + + /** + * Default weight value is 1. + */ + int weight(); + + /** + * Set the weight of the dependency. + */ + Dependency weight(int weight); + + /** + * Save the dependency. + */ + void save(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java new file mode 100644 index 00000000000..34a91f80756 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java @@ -0,0 +1,131 @@ +/* + * 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.api.batch.sensor.dependency.internal; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorStorage; +import org.sonar.api.batch.sensor.dependency.Dependency; + +import javax.annotation.Nullable; + +public class DefaultDependency implements Dependency { + + private final SensorStorage storage; + private InputFile from; + private InputFile to; + private int weight = 1; + private boolean saved = false; + + public DefaultDependency() { + this.storage = null; + } + + public DefaultDependency(@Nullable SensorStorage storage) { + this.storage = storage; + } + + @Override + public Dependency from(InputFile from) { + Preconditions.checkNotNull(from, "InputFile should be non null"); + this.from = from; + return this; + } + + @Override + public Dependency to(InputFile to) { + Preconditions.checkNotNull(to, "InputFile should be non null"); + this.to = to; + return this; + } + + @Override + public Dependency weight(int weight) { + Preconditions.checkArgument(weight > 1, "weight should be greater than 1"); + this.weight = weight; + return this; + } + + @Override + public void save() { + Preconditions.checkNotNull(this.storage, "No persister on this object"); + Preconditions.checkState(!saved, "This dependency was already saved"); + Preconditions.checkState(!this.from.equals(this.to), "From and To can't be the same inputFile"); + Preconditions.checkNotNull(this.from, "From inputFile can't be null"); + Preconditions.checkNotNull(this.to, "To inputFile can't be null"); + storage.store((Dependency) this); + this.saved = true; + } + + @Override + public InputFile from() { + return this.from; + } + + @Override + public InputFile to() { + return this.to; + } + + @Override + public int weight() { + return this.weight; + } + + // For testing purpose + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + DefaultDependency rhs = (DefaultDependency) obj; + return new EqualsBuilder() + .append(from, rhs.from) + .append(to, rhs.to) + .append(weight, rhs.weight) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(27, 45). + append(from). + append(to). + append(weight). + toHashCode(); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/package-info.java similarity index 70% rename from sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java rename to sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/package-info.java index 0869ee6cde6..d45dd459268 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/dependency/OutgoingDependency.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/package-info.java @@ -17,26 +17,5 @@ * 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 int weight; - - public OutgoingDependency(InputFile to, int weight) { - this.to = to; - this.weight = weight; - } - - public InputFile to() { - return to; - } - - public int weight() { - return weight; - } - -} +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.api.batch.sensor.dependency.internal; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/package-info.java new file mode 100644 index 00000000000..e42fabac294 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.api.batch.sensor.dependency; -- 2.39.5