From: Julien HENRY Date: Fri, 19 Sep 2014 16:08:26 +0000 (+0200) Subject: SONAR-5389 Refactor new issue API X-Git-Tag: 5.0-RC1~968 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=14f36bfb794a37ac67b643924e8608421a8f549c;p=sonarqube.git SONAR-5389 Refactor new issue API --- diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java index d2dd4d2d32c..1b565ff56b3 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java @@ -29,8 +29,8 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; -import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder; import org.sonar.api.batch.sensor.measure.Measure; @@ -63,14 +63,14 @@ public class JavaCpdEngineTest { private DefaultDuplicationBuilder duplicationBuilder; private FileLinesContextFactory contextFactory; private FileLinesContext linesContext; - private SensorStorage persister = mock(SensorStorage.class); + private SensorStorage storage = mock(SensorStorage.class); @Before public void before() throws IOException { when(context.newMeasure()).then(new Answer() { @Override public Measure answer(InvocationOnMock invocation) throws Throwable { - return new DefaultMeasure(persister); + return new DefaultMeasure(storage); } }); inputFile = new DeprecatedDefaultInputFile("foo", "src/main/java/Foo.java"); @@ -97,9 +97,9 @@ public class JavaCpdEngineTest { List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214))); JavaCpdEngine.save(context, inputFile, groups, contextFactory); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200)); InOrder inOrder = Mockito.inOrder(duplicationBuilder); inOrder.verify(duplicationBuilder).originBlock(5, 204); @@ -118,9 +118,9 @@ public class JavaCpdEngineTest { List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414))); JavaCpdEngine.save(context, inputFile, groups, contextFactory); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(400)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(400)); InOrder inOrder = Mockito.inOrder(duplicationBuilder); inOrder.verify(duplicationBuilder).originBlock(5, 204); @@ -133,9 +133,9 @@ public class JavaCpdEngineTest { List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224))); JavaCpdEngine.save(context, inputFile, groups, contextFactory); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200)); InOrder inOrder = Mockito.inOrder(duplicationBuilder); inOrder.verify(duplicationBuilder).originBlock(5, 204); @@ -157,9 +157,9 @@ public class JavaCpdEngineTest { newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214))); JavaCpdEngine.save(context, inputFile, groups, contextFactory); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(210)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(210)); InOrder inOrder = Mockito.inOrder(duplicationBuilder); inOrder.verify(duplicationBuilder).originBlock(5, 204); diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java index 041bdd94efd..01b2f8168bd 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java @@ -50,11 +50,11 @@ public class CreateIssueByInternalKeySensor implements Sensor { ActiveRule rule = context.activeRules().findByInternalKey(XooRulesDefinition.XOO_REPOSITORY, context.settings().getString(INTERNAL_KEY_PROPERTY)); if (rule != null) { - context.addIssue(context.issueBuilder() + context.newIssue() .ruleKey(rule.ruleKey()) .onFile(file) .message("This issue is generated on each file") - .build()); + .save(); } } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java index 6763d0308c8..7a2161b1049 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java @@ -51,11 +51,11 @@ public class OneIssueOnDirPerFileSensor implements Sensor { RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY); InputDir inputDir = context.fileSystem().inputDir(file.file().getParentFile()); if (inputDir != null) { - context.addIssue(context.issueBuilder() + context.newIssue() .ruleKey(ruleKey) .onDir(inputDir) .message("This issue is generated for file " + file.relativePath()) - .build()); + .save(); } } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java index e9fab499c18..7a11c639e38 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java @@ -23,7 +23,7 @@ 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.api.batch.sensor.issue.IssueBuilder; +import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.rule.RuleKey; import org.sonar.xoo.Xoo; @@ -53,16 +53,16 @@ public class OneIssuePerLineSensor implements Sensor { private void createIssues(InputFile file, SensorContext context) { RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY); - IssueBuilder issueBuilder = context.issueBuilder(); + String severity = context.settings().getString(FORCE_SEVERITY_PROPERTY); for (int line = 1; line <= file.lines(); line++) { - context.addIssue(issueBuilder + context.newIssue() .ruleKey(ruleKey) .onFile(file) .atLine(line) .effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY)) - .severity(context.settings().getString(FORCE_SEVERITY_PROPERTY)) + .overrideSeverity(severity != null ? Severity.valueOf(severity) : null) .message("This issue is generated on each line") - .build()); + .save(); } } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/MeasureSensorTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/MeasureSensorTest.java index 11be6dfcccc..2e04e8184db 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/MeasureSensorTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/MeasureSensorTest.java @@ -30,8 +30,8 @@ import org.mockito.stubbing.Answer; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; @@ -58,7 +58,7 @@ public class MeasureSensorTest { private File baseDir; private MetricFinder metricFinder; - private SensorStorage persister; + private SensorStorage storage; @Before public void prepare() throws IOException { @@ -67,11 +67,11 @@ public class MeasureSensorTest { sensor = new MeasureSensor(metricFinder); fileSystem = new DefaultFileSystem(); when(context.fileSystem()).thenReturn(fileSystem); - persister = mock(SensorStorage.class); + storage = mock(SensorStorage.class); when(context.newMeasure()).then(new Answer() { @Override public DefaultMeasure answer(InvocationOnMock invocation) throws Throwable { - return new DefaultMeasure(persister); + return new DefaultMeasure(storage); } }); } @@ -106,11 +106,11 @@ public class MeasureSensorTest { sensor.execute(context); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.NCLOC).onFile(inputFile).withValue(12)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.BRANCH_COVERAGE).onFile(inputFile).withValue(5.3)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.TECHNICAL_DEBT).onFile(inputFile).withValue(300L)); - verify(persister).store(new DefaultMeasure().forMetric(booleanMetric).onFile(inputFile).withValue(true)); - verify(persister).store(new DefaultMeasure().forMetric(CoreMetrics.COMMENT_LINES_DATA).onFile(inputFile).withValue("1=1,2=1")); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.NCLOC).onFile(inputFile).withValue(12)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.BRANCH_COVERAGE).onFile(inputFile).withValue(5.3)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.TECHNICAL_DEBT).onFile(inputFile).withValue(300L)); + verify(storage).store(new DefaultMeasure().forMetric(booleanMetric).onFile(inputFile).withValue(true)); + verify(storage).store(new DefaultMeasure().forMetric(CoreMetrics.COMMENT_LINES_DATA).onFile(inputFile).withValue("1=1,2=1")); } 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 d73f1fcab1a..70393580638 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 @@ -216,7 +216,7 @@ public class DefaultIndex extends SonarIndex { 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."); + LOG.warn("Metric " + metric.key() + " is an internal metric computed by SonarQube. Please update your plugin."); return measure; } measure.setMetric(metric); 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 69b1dad2385..437d778b7fd 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 @@ -56,8 +56,8 @@ import org.sonar.batch.protocol.input.ProjectReferentials; import org.sonar.batch.referential.GlobalReferentialsLoader; import org.sonar.batch.referential.ProjectReferentialsLoader; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.batch.scan2.AnalyzerIssueCache; -import org.sonar.batch.scan2.NewMeasureCache; +import org.sonar.batch.scan2.IssueCache; +import org.sonar.batch.scan2.MeasureCache; import org.sonar.batch.scan2.ProjectScanContainer; import org.sonar.batch.scan2.ScanTaskObserver; import org.sonar.batch.symbol.SymbolData; @@ -240,11 +240,11 @@ public class BatchMediumTester { @Override public void scanTaskCompleted(ProjectScanContainer container) { LOG.info("Store analysis results in memory for later assertions in medium test"); - for (Issue issue : container.getComponentByType(AnalyzerIssueCache.class).all()) { + for (Issue issue : container.getComponentByType(IssueCache.class).all()) { issues.add(issue); } - for (DefaultMeasure measure : container.getComponentByType(NewMeasureCache.class).all()) { + for (DefaultMeasure measure : container.getComponentByType(MeasureCache.class).all()) { measures.add(measure); } 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 8a225c94dc0..f11c6b82f88 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 @@ -33,6 +33,7 @@ import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.test.TestCase; import org.sonar.api.batch.sensor.test.internal.DefaultTestCase; @@ -62,7 +63,6 @@ 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; /** @@ -100,7 +100,7 @@ public class SensorContextAdapter extends BaseSensorContext { } @Override - public void store(Measure measure) { + public void store(Measure measure) { org.sonar.api.measures.Metric m = metricFinder.findByKey(measure.metric().key()); if (m == null) { throw new IllegalStateException("Unknow metric with key: " + measure.metric().key()); @@ -155,7 +155,7 @@ public class SensorContextAdapter extends BaseSensorContext { } @Override - public boolean addIssue(Issue issue) { + public void store(Issue issue) { Resource r; InputPath inputPath = issue.inputPath(); if (inputPath != null) { @@ -169,12 +169,13 @@ public class SensorContextAdapter extends BaseSensorContext { } Issuable issuable = perspectives.as(Issuable.class, r); if (issuable == null) { - return false; + return; } - return issuable.addIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); + issuable.addIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); } public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) { + Severity overridenSeverity = issue.overridenSeverity(); return new org.sonar.core.issue.DefaultIssueBuilder() .componentKey(componentKey) .projectKey(projectKey) @@ -182,7 +183,7 @@ public class SensorContextAdapter extends BaseSensorContext { .effortToFix(issue.effortToFix()) .line(issue.line()) .message(issue.message()) - .severity(issue.severity()) + .severity(overridenSeverity != null ? overridenSeverity.name() : null) .build(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java index 824b229897a..1dad931f8b5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java @@ -27,6 +27,7 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.config.Settings; import org.sonar.api.utils.ZipUtils; @@ -42,13 +43,13 @@ public final class AnalysisPublisher { private static final Logger LOG = LoggerFactory.getLogger(AnalysisPublisher.class); private final Settings settings; private final FileSystem fs; - private final NewMeasureCache measureCache; + private final MeasureCache measureCache; private final ProjectDefinition def; - private final AnalyzerIssueCache issueCache; + private final IssueCache issueCache; public AnalysisPublisher(ProjectDefinition def, Settings settings, FileSystem fs, - NewMeasureCache measureCache, - AnalyzerIssueCache analyzerIssueCache) { + MeasureCache measureCache, + IssueCache analyzerIssueCache) { this.def = def; this.settings = settings; this.fs = fs; @@ -108,9 +109,12 @@ public final class AnalysisPublisher { } jsonWriter.prop("message", issue.message()) .prop("effortToFix", issue.effortToFix()) - .prop("line", issue.line()) - .prop("severity", issue.severity()) - .endObject(); + .prop("line", issue.line()); + Severity overridenSeverity = issue.overridenSeverity(); + if (overridenSeverity != null) { + jsonWriter.prop("severity", overridenSeverity.name()); + } + jsonWriter.endObject(); } jsonWriter.endArray() .endObject() diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java deleted file mode 100644 index 9b0fd3f6d60..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerIssueCache.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.scan2; - -import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; - -import org.sonar.api.BatchComponent; -import org.sonar.batch.index.Cache; -import org.sonar.batch.index.Caches; - -/** - * Shared issues among all project modules - */ -public class AnalyzerIssueCache implements BatchComponent { - - // project key -> resource key -> issue key -> issue - private final Cache cache; - - public AnalyzerIssueCache(Caches caches) { - cache = caches.createCache("issues"); - } - - public Iterable byComponent(String projectKey, String resourceKey) { - return cache.values(projectKey, resourceKey); - } - - public Iterable all() { - return cache.values(); - } - - public AnalyzerIssueCache put(String projectKey, String resourceKey, DefaultIssue issue) { - cache.put(projectKey, resourceKey, issue.key(), issue); - return this; - } - - public Iterable byModule(String projectKey) { - return cache.values(projectKey); - } - -} 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 63135f0dc03..09ed5a9f133 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 @@ -24,15 +24,15 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRules; -import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder; import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; -import org.sonar.api.batch.sensor.issue.IssueBuilder; -import org.sonar.api.batch.sensor.issue.internal.DefaultIssueBuilder; +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; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder; @@ -56,7 +56,7 @@ import java.util.List; * @author julien * */ -public abstract class BaseSensorContext implements SensorContext, SensorStorage> { +public abstract class BaseSensorContext implements SensorContext, SensorStorage { private final Settings settings; private final FileSystem fs; @@ -96,8 +96,8 @@ public abstract class BaseSensorContext implements SensorContext, SensorStorage< } @Override - public IssueBuilder issueBuilder() { - return new DefaultIssueBuilder(); + public Issue newIssue() { + return new DefaultIssue(this); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContext.java index 5c9559a90eb..545a8d17626 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContext.java @@ -36,7 +36,7 @@ import java.util.Map; public class DefaultFileLinesContext implements FileLinesContext { - private final NewMeasureCache measureCache; + private final MeasureCache measureCache; private final InputFile inputFile; /** @@ -46,7 +46,7 @@ public class DefaultFileLinesContext implements FileLinesContext { private String projectKey; private MetricFinder metricFinder; - public DefaultFileLinesContext(MetricFinder metricFinder, NewMeasureCache measureCache, String projectKey, InputFile inputFile) { + public DefaultFileLinesContext(MetricFinder metricFinder, MeasureCache measureCache, String projectKey, InputFile inputFile) { this.metricFinder = metricFinder; this.projectKey = projectKey; Preconditions.checkNotNull(measureCache); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java index 78086efdd8e..c93fb84312f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java @@ -30,12 +30,12 @@ import org.sonar.batch.scan.filesystem.InputPathCache; public class DefaultFileLinesContextFactory implements FileLinesContextFactory { - private final NewMeasureCache measureCache; + private final MeasureCache measureCache; private final MetricFinder metricFinder; private final ProjectDefinition def; private InputPathCache fileCache; - public DefaultFileLinesContextFactory(InputPathCache fileCache, FileSystem fs, MetricFinder metricFinder, NewMeasureCache measureCache, + public DefaultFileLinesContextFactory(InputPathCache fileCache, FileSystem fs, MetricFinder metricFinder, MeasureCache measureCache, ProjectDefinition def) { this.fileCache = fileCache; this.metricFinder = metricFinder; diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultIssueValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultIssueValueCoder.java new file mode 100644 index 00000000000..61ce8f9166f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultIssueValueCoder.java @@ -0,0 +1,104 @@ +/* + * 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.scan2; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +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.DefaultInputDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.issue.Issue.Severity; +import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.batch.scan.filesystem.InputPathCache; + +class DefaultIssueValueCoder implements ValueCoder { + + private final InputPathCache inputPathCache; + + public DefaultIssueValueCoder(InputPathCache inputPathCache) { + this.inputPathCache = inputPathCache; + } + + @Override + public void put(Value value, Object object, CoderContext context) { + DefaultIssue issue = (DefaultIssue) object; + value.putString(issue.key()); + InputPath inputPath = issue.inputPath(); + if (inputPath != null) { + if (inputPath instanceof InputDir) { + value.put(0); + value.putString(((DefaultInputDir) inputPath).moduleKey()); + value.putString(inputPath.relativePath()); + } else { + value.put(1); + value.putString(((DefaultInputFile) inputPath).moduleKey()); + value.putString(inputPath.relativePath()); + value.put(issue.line()); + } + } else { + value.putNull(); + } + value.put(issue.message()); + value.put(issue.effortToFix()); + value.put(issue.ruleKey().repository()); + value.put(issue.ruleKey().rule()); + Severity overridenSeverity = issue.overridenSeverity(); + if (overridenSeverity == null) { + value.putNull(); + } else { + value.put(overridenSeverity.ordinal()); + } + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + DefaultIssue newIssue = new DefaultIssue(null); + newIssue.withKey(value.getString()); + if (value.isNull(true)) { + newIssue.onProject(); + } else { + int type = value.getInt(); + String moduleKey = value.getString(); + String relativePath = value.getString(); + if (type == 0) { + InputDir dir = inputPathCache.getDir(moduleKey, relativePath); + newIssue.onDir(dir); + } else { + InputFile f = inputPathCache.getFile(moduleKey, relativePath); + newIssue.onFile(f); + if (!value.isNull(true)) { + newIssue.atLine(value.getInt()); + } + } + } + newIssue.message(value.getString()); + newIssue.effortToFix(value.isNull(true) ? null : value.getDouble()); + String repo = value.getString(); + String rule = value.getString(); + newIssue.ruleKey(RuleKey.of(repo, rule)); + newIssue.overrideSeverity(value.isNull(true) ? null : Severity.values()[value.getInt()]); + return newIssue; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java index 53ab539df7b..e0beb814130 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java @@ -51,7 +51,7 @@ class DefaultMeasureValueCoder implements ValueCoder { } else { value.putNull(); } - value.putUTF(m.metric().key()); + value.putString(m.metric().key()); value.put(m.value()); } 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 51c13e2bf93..e02999d2375 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 @@ -71,8 +71,8 @@ public class DefaultSensorContext extends BaseSensorContext { CoreMetrics.FILE_TANGLE_INDEX, CoreMetrics.FILE_TANGLES ); - private final NewMeasureCache measureCache; - private final AnalyzerIssueCache issueCache; + private final MeasureCache measureCache; + private final IssueCache issueCache; private final ProjectDefinition def; private final ActiveRules activeRules; private final IssueFilters issueFilters; @@ -80,7 +80,7 @@ public class DefaultSensorContext extends BaseSensorContext { private final CoveragePerTestCache coveragePerTestCache; private final DependencyCache dependencyCache; - public DefaultSensorContext(ProjectDefinition def, NewMeasureCache measureCache, AnalyzerIssueCache issueCache, + public DefaultSensorContext(ProjectDefinition def, MeasureCache measureCache, IssueCache issueCache, Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache, BlockCache blockCache, DuplicationCache duplicationCache, TestCaseCache testCaseCache, CoveragePerTestCache coveragePerTestCache, DependencyCache dependencyCache) { super(settings, fs, activeRules, componentDataCache, blockCache, duplicationCache); @@ -95,10 +95,10 @@ public class DefaultSensorContext extends BaseSensorContext { } @Override - public void store(Measure newMeasure) { + public void store(Measure newMeasure) { DefaultMeasure measure = (DefaultMeasure) newMeasure; if (INTERNAL_METRICS.contains(measure.metric())) { - LOG.warn("Metric " + measure.metric() + " is an internal metric computed by SonarQube. Please update your plugin."); + LOG.warn("Metric " + measure.metric().key() + " is an internal metric computed by SonarQube. Please update your plugin."); return; } InputFile inputFile = measure.inputFile(); @@ -110,7 +110,7 @@ public class DefaultSensorContext extends BaseSensorContext { } @Override - public boolean addIssue(Issue issue) { + public void store(Issue issue) { String resourceKey; if (issue.inputPath() != null) { resourceKey = ComponentKeys.createEffectiveKey(def.getKey(), issue.inputPath()); @@ -121,7 +121,8 @@ public class DefaultSensorContext extends BaseSensorContext { DefaultActiveRule activeRule = (DefaultActiveRule) activeRules.find(ruleKey); if (activeRule == null) { // rule does not exist or is not enabled -> ignore the issue - return false; + LOG.debug("Rule {} does not exists or is not enabled. Issue {} is ignored.", issue.ruleKey(), issue); + return; } if (Strings.isNullOrEmpty(activeRule.name()) && Strings.isNullOrEmpty(issue.message())) { throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey)); @@ -129,21 +130,16 @@ public class DefaultSensorContext extends BaseSensorContext { updateIssue((DefaultIssue) issue, activeRule); - if (issueFilters.accept(SensorContextAdapter.toDefaultIssue(def.getKey(), resourceKey, issue), null)) { - issueCache.put(def.getKey(), resourceKey, (DefaultIssue) issue); - return true; + if (!issueFilters.accept(SensorContextAdapter.toDefaultIssue(def.getKey(), resourceKey, issue), null)) { + LOG.debug("Issue {} was excluded by some filters.", issue); + return; } - - return false; + issueCache.put(def.getKey(), resourceKey, (DefaultIssue) issue); } private void updateIssue(DefaultIssue issue, DefaultActiveRule activeRule) { if (Strings.isNullOrEmpty(issue.message())) { - issue.setMessage(activeRule.name()); - } - - if (issue.severity() == null) { - issue.setSeverity(activeRule.severity()); + issue.message(activeRule.name()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/IssueCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/IssueCache.java new file mode 100644 index 00000000000..5de18c1b8c3 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/IssueCache.java @@ -0,0 +1,58 @@ +/* + * 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.scan2; + +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Caches; +import org.sonar.batch.scan.filesystem.InputPathCache; + +/** + * Shared issues among all project modules + */ +public class IssueCache implements BatchComponent { + + // project key -> resource key -> issue key -> issue + private final Cache cache; + + public IssueCache(Caches caches, InputPathCache inputPathCache) { + caches.registerValueCoder(DefaultIssue.class, new DefaultIssueValueCoder(inputPathCache)); + cache = caches.createCache("issues"); + } + + public Iterable byComponent(String projectKey, String resourceKey) { + return cache.values(projectKey, resourceKey); + } + + public Iterable all() { + return cache.values(); + } + + public IssueCache put(String projectKey, String resourceKey, DefaultIssue issue) { + cache.put(projectKey, resourceKey, issue.key(), issue); + return this; + } + + public Iterable byModule(String projectKey) { + return cache.values(projectKey); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java new file mode 100644 index 00000000000..57fe7dddbc0 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/MeasureCache.java @@ -0,0 +1,75 @@ +/* + * 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.scan2; + +import com.google.common.base.Preconditions; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +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; + +/** + * Cache of all measures. This cache is shared amongst all project modules. + */ +public class MeasureCache implements BatchComponent { + + // project key -> component key -> metric key -> measure + private final Cache cache; + + public MeasureCache(Caches caches, MetricFinder metricFinder, InputPathCache inputPathCache) { + caches.registerValueCoder(DefaultMeasure.class, new DefaultMeasureValueCoder(metricFinder, inputPathCache)); + cache = caches.createCache("measures"); + } + + public Iterable> entries() { + return cache.entries(); + } + + public Iterable byModule(String projectKey) { + return cache.values(projectKey); + } + + public DefaultMeasure byMetric(String projectKey, String resourceKey, String metricKey) { + return cache.get(projectKey, resourceKey, metricKey); + } + + public MeasureCache put(String projectKey, String resourceKey, DefaultMeasure measure) { + Preconditions.checkNotNull(projectKey); + Preconditions.checkNotNull(resourceKey); + Preconditions.checkNotNull(measure); + cache.put(projectKey, resourceKey, measure.metric().key(), measure); + return this; + } + + public boolean contains(String projectKey, String resourceKey, DefaultMeasure measure) { + Preconditions.checkNotNull(projectKey); + Preconditions.checkNotNull(resourceKey); + Preconditions.checkNotNull(measure); + return cache.containsKey(projectKey, resourceKey, measure.metric().key()); + } + + public Iterable all() { + return cache.values(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/NewMeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/NewMeasureCache.java deleted file mode 100644 index 361ed8c181b..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/NewMeasureCache.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.scan2; - -import com.google.common.base.Preconditions; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -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; - -/** - * Cache of all measures. This cache is shared amongst all project modules. - */ -public class NewMeasureCache implements BatchComponent { - - // project key -> component key -> metric key -> measure - private final Cache cache; - - public NewMeasureCache(Caches caches, MetricFinder metricFinder, InputPathCache inputPathCache) { - caches.registerValueCoder(DefaultMeasure.class, new DefaultMeasureValueCoder(metricFinder, inputPathCache)); - cache = caches.createCache("measures"); - } - - public Iterable> entries() { - return cache.entries(); - } - - public Iterable byModule(String projectKey) { - return cache.values(projectKey); - } - - public DefaultMeasure byMetric(String projectKey, String resourceKey, String metricKey) { - return cache.get(projectKey, resourceKey, metricKey); - } - - public NewMeasureCache put(String projectKey, String resourceKey, DefaultMeasure measure) { - Preconditions.checkNotNull(projectKey); - Preconditions.checkNotNull(resourceKey); - Preconditions.checkNotNull(measure); - cache.put(projectKey, resourceKey, measure.metric().key(), measure); - return this; - } - - public boolean contains(String projectKey, String resourceKey, DefaultMeasure measure) { - Preconditions.checkNotNull(projectKey); - Preconditions.checkNotNull(resourceKey); - Preconditions.checkNotNull(measure); - return cache.containsKey(projectKey, resourceKey, measure.metric().key()); - } - - public Iterable all() { - return cache.values(); - } - -} 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 09b5bdf8db8..8eeb6c5b113 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 @@ -101,14 +101,14 @@ public class ProjectScanContainer extends ComponentContainer { DefaultLanguagesReferential.class, // Measures - NewMeasureCache.class, + MeasureCache.class, // file system InputPathCache.class, PathResolver.class, // issues - AnalyzerIssueCache.class, + IssueCache.class, // Syntax highlighting and symbols ComponentDataCache.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java index 0cbe96dd48f..146761ad94f 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; import org.sonar.batch.protocol.input.ActiveRule; @@ -91,7 +92,7 @@ public class IssuesMediumTest { .property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL") .start(); - assertThat(result.issues().iterator().next().severity()).isEqualTo("CRITICAL"); + assertThat(result.issues().iterator().next().overridenSeverity()).isEqualTo(Severity.CRITICAL); } @Test 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 a93e87dfe2a..865e3b50f6b 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 @@ -31,7 +31,7 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; 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.issue.internal.DefaultIssueBuilder; +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; @@ -84,7 +84,7 @@ public class SensorContextAdapterTest { assertThat(adaptor.fileSystem()).isEqualTo(fs); assertThat(adaptor.settings()).isEqualTo(settings); - assertThat(adaptor.issueBuilder()).isNotNull(); + assertThat(adaptor.newIssue()).isNotNull(); assertThat(adaptor.newMeasure()).isNotNull(); } @@ -116,13 +116,12 @@ public class SensorContextAdapterTest { when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true); - adaptor.addIssue(new DefaultIssueBuilder() + adaptor.store(new DefaultIssue() .onFile(file) .ruleKey(RuleKey.of("foo", "bar")) .message("Foo") .atLine(3) - .effortToFix(10.0) - .build()); + .effortToFix(10.0)); Issue issue = argumentCaptor.getValue(); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar")); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/Sensor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/Sensor.java index f01ab0fa900..a0be6e447ab 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/Sensor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/Sensor.java @@ -19,7 +19,6 @@ */ package org.sonar.api.batch.sensor; -import com.google.common.annotations.Beta; import org.sonar.api.BatchExtension; /** @@ -32,9 +31,8 @@ import org.sonar.api.BatchExtension; * For example the Cobertura Sensor parses Cobertura report and saves the first-level of measures on files. *

* - * @since 4.4 + * @since 5.0 */ -@Beta public interface Sensor extends BatchExtension { /** 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 016bda15f94..b79471f7975 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 @@ -19,7 +19,6 @@ */ package org.sonar.api.batch.sensor; -import com.google.common.annotations.Beta; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.ActiveRules; @@ -28,7 +27,6 @@ import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.api.batch.sensor.issue.Issue; -import org.sonar.api.batch.sensor.issue.IssueBuilder; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder; import org.sonar.api.batch.sensor.test.TestCase; @@ -41,9 +39,9 @@ import java.io.Serializable; import java.util.List; /** - * @since 4.4 + * See {@link Sensor#execute(SensorContext)} + * @since 5.0 */ -@Beta public interface SensorContext { /** @@ -64,26 +62,16 @@ public interface SensorContext { // ----------- MEASURES -------------- /** - * Builder to create a new {@link Measure}. + * Fluent builder to create a new {@link Measure}. Don't forget to call {@link Measure#save()} once all parameters are provided. */ Measure newMeasure(); // ----------- ISSUES -------------- /** - * Builder to create a new {@link Issue}. + * Fluent builder to create a new {@link Issue}. Don't forget to call {@link Issue#save()} once all parameters are provided. */ - IssueBuilder issueBuilder(); - - /** - * Add an issue. Use {@link #issueBuilder()} to create the new issue. - * @return true if the new issue is registered, false if: - *
    - *
  • the rule does not exist
  • - *
  • the rule is disabled in the Quality profile
  • - *
- */ - boolean addIssue(Issue issue); + Issue newIssue(); // ------------ HIGHLIGHTING ------------ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java index 9fcd030e553..a2187c13c49 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java @@ -19,16 +19,15 @@ */ package org.sonar.api.batch.sensor; -import com.google.common.annotations.Beta; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.Metric; /** * Describe what an {@link Sensor} is doing. Information may be used by the platform * to log interesting information or perform some optimization. - * @since 4.4 + * See {@link Sensor#describe(SensorDescriptor)} + * @since 5.0 */ -@Beta public interface SensorDescriptor { /** 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 c48035c8101..ce2bbc1dbd3 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,11 +19,17 @@ */ package org.sonar.api.batch.sensor; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.measure.Measure; + /** * Interface for storing data computed by sensors. + * @since 5.0 */ -public interface SensorStorage { +public interface SensorStorage { + + void store(Measure measure); - void store(G data); + void store(Issue issue); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java index a8d2512542a..e126ddef95b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java @@ -20,25 +20,35 @@ package org.sonar.api.batch.sensor.issue; import com.google.common.annotations.Beta; +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.sensor.Sensor; import org.sonar.api.rule.RuleKey; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; /** - * Issue reported by an {@link Sensor} + * Represents an issue detected by a {@link Sensor}. * - * @since 4.4 + * @since 5.0 */ @Beta public interface Issue { + public enum Severity { + INFO, + MINOR, + MAJOR, + CRITICAL, + BLOCKER; + } + /** - * The {@link InputPath} this issue belongs to. Returns null if issue is global to the project. + * The {@link RuleKey} of the issue. */ - @CheckForNull - InputPath inputPath(); + Issue ruleKey(RuleKey ruleKey); /** * The {@link RuleKey} of this issue. @@ -46,10 +56,31 @@ public interface Issue { RuleKey ruleKey(); /** - * Message of the issue. + * The {@link InputFile} the issue belongs to. For global issues call {@link #onProject()}. + */ + Issue onFile(InputFile file); + + /** + * The {@link InputDir} the issue belongs to. For global issues call {@link #onProject()}. + */ + Issue onDir(InputDir inputDir); + + /** + * Tell that the issue is global to the project. + */ + Issue onProject(); + + /** + * The {@link InputPath} this issue belongs to. Returns null if issue is global to the project. */ @CheckForNull - String message(); + InputPath inputPath(); + + /** + * Line of the issue. Only available for {@link #onFile(InputFile)} issues. + * If no line is specified it means that issue is global to the file. + */ + Issue atLine(int line); /** * Line of the issue. Null for global issues and issues on directories. Can also be null @@ -58,6 +89,11 @@ public interface Issue { @CheckForNull Integer line(); + /** + * Effort to fix the issue. + */ + Issue effortToFix(@Nullable Double effortToFix); + /** * Effort to fix the issue. Used by technical debt model. */ @@ -65,10 +101,32 @@ public interface Issue { Double effortToFix(); /** - * See constants in {@link org.sonar.api.rule.Severity}. - * Can be null before issue is saved. Means to use severity configured in quality profile. + * Message of the issue. + */ + Issue message(String message); + + /** + * Message of the issue. */ @CheckForNull - String severity(); + String message(); + + /** + * Override severity of the issue. + * Setting a null value or not calling this method means to use severity configured in quality profile. + */ + Issue overrideSeverity(@Nullable Severity severity); + + /** + * Overriden severity. + */ + @CheckForNull + Severity overridenSeverity(); + + /** + * Save the issue. If rule key is unknow or rule not enabled in the current quality profile then a warning is logged but no exception + * is thrown. + */ + void save(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueBuilder.java deleted file mode 100644 index f6b4fa85aaa..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueBuilder.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.issue; - -import com.google.common.annotations.Beta; -import org.sonar.api.batch.fs.InputDir; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; - -import javax.annotation.Nullable; - -/** - * Builder for {@link Issue}. - * - * @since 4.4 - */ -@Beta -public interface IssueBuilder { - - /** - * The {@link RuleKey} of the issue. - */ - IssueBuilder ruleKey(RuleKey ruleKey); - - /** - * The {@link InputFile} the issue belongs to. For global issues call {@link #onProject()}. - */ - IssueBuilder onFile(InputFile file); - - /** - * The {@link InputDir} the issue belongs to. For global issues call {@link #onProject()}. - */ - IssueBuilder onDir(InputDir inputDir); - - /** - * Tell that the issue is global to the project. - */ - IssueBuilder onProject(); - - /** - * Line of the issue. Only available for {@link #onFile(InputFile)} issues. - * If no line is specified it means that issue is global to the file. - */ - IssueBuilder atLine(int line); - - /** - * Effort to fix the issue. - */ - IssueBuilder effortToFix(@Nullable Double effortToFix); - - /** - * Message of the issue. - */ - IssueBuilder message(String message); - - /** - * Severity of the issue. See {@link Severity}. - * Setting a null value means to use severity configured in quality profile. - */ - IssueBuilder severity(@Nullable String severity); - - /** - * Build the issue. After call of this method the builder is cleaned and can be used to build another issue. - */ - Issue build(); - -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java index f3b56f73b6c..23148378f35 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java @@ -23,82 +23,152 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; +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.sensor.SensorStorage; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.rule.RuleKey; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.io.Serializable; import java.util.UUID; -public class DefaultIssue implements Issue, Serializable { +public class DefaultIssue implements Issue { - private final String key; - private final InputPath inputPath; - private final RuleKey ruleKey; + private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null"; + private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null"; + private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called"; + private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called"; + private String key; + private boolean onProject = false; + private InputPath path; + private RuleKey ruleKey; private String message; - private final Integer line; - private final Double effortToFix; - private String severity; - - DefaultIssue(DefaultIssueBuilder builder) { - Preconditions.checkNotNull(builder.ruleKey, "ruleKey is mandatory on issue"); - this.inputPath = builder.path; - this.ruleKey = builder.ruleKey; - this.message = builder.message; - this.line = builder.line; - this.effortToFix = builder.effortToFix; - this.severity = builder.severity; - this.key = builder.key == null ? UUID.randomUUID().toString() : builder.key; - Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); + private Integer line; + private Double effortToFix; + private Severity overridenSeverity; + private final SensorStorage storage; + + public DefaultIssue() { + this.storage = null; } - public String key() { - return key; + public DefaultIssue(SensorStorage storage) { + this.storage = storage; } @Override - @Nullable - public InputPath inputPath() { - return inputPath; + public DefaultIssue ruleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + return this; } @Override - public RuleKey ruleKey() { - return ruleKey; + public DefaultIssue onFile(InputFile file) { + Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); + Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); + Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL); + this.path = file; + return this; } - @CheckForNull @Override - public String message() { - return message; + public DefaultIssue onDir(InputDir dir) { + Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); + Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); + Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL); + this.path = dir; + return this; + } + + @Override + public DefaultIssue onProject() { + Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); + Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); + this.onProject = true; + return this; + } + + @Override + public DefaultIssue atLine(int line) { + Preconditions.checkState(this.path != null && this.path instanceof InputFile, "atLine should be called after onFile"); + this.line = line; + return this; + } + + @Override + public DefaultIssue effortToFix(@Nullable Double effortToFix) { + this.effortToFix = effortToFix; + return this; } - public void setMessage(String message) { + @Override + public DefaultIssue message(String message) { this.message = message; + return this; + } + + @Override + public Issue overrideSeverity(@Nullable Severity severity) { + this.overridenSeverity = severity; + return this; + } + + @Override + public RuleKey ruleKey() { + return this.ruleKey; + } + + @CheckForNull + @Override + public InputPath inputPath() { + return this.path; } @Override public Integer line() { - return line; + return this.line; + } + + @Override + public String message() { + return this.message; + } + + @Override + public Severity overridenSeverity() { + return this.overridenSeverity; } @Override - @Nullable public Double effortToFix() { - return effortToFix; + return this.effortToFix; + } + + public String key() { + return this.key; } @Override - @CheckForNull - public String severity() { - return severity; + public void save() { + Preconditions.checkNotNull(this.storage, "No persister on this object"); + Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue"); + if (this.key == null) { + this.key = UUID.randomUUID().toString(); + } + Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); + + storage.store(this); } - public void setSeverity(String severity) { - this.severity = severity; + /** + * For testing only. + */ + public DefaultIssue withKey(String key) { + this.key = key; + return this; } @Override @@ -122,5 +192,4 @@ public class DefaultIssue implements Issue, Serializable { public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } - } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueBuilder.java deleted file mode 100644 index 10c0c71f5be..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueBuilder.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.issue.internal; - -import com.google.common.base.Preconditions; -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.sensor.issue.IssueBuilder; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; - -import javax.annotation.Nullable; - -public class DefaultIssueBuilder implements IssueBuilder { - - private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null"; - private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null"; - private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called"; - private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called"; - String key; - boolean onProject = false; - InputPath path; - RuleKey ruleKey; - String message; - Integer line; - Double effortToFix; - String severity; - - @Override - public DefaultIssueBuilder ruleKey(RuleKey ruleKey) { - this.ruleKey = ruleKey; - return this; - } - - @Override - public DefaultIssueBuilder onFile(InputFile file) { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL); - this.path = file; - return this; - } - - @Override - public DefaultIssueBuilder onDir(InputDir dir) { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL); - this.path = dir; - return this; - } - - @Override - public DefaultIssueBuilder onProject() { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - this.onProject = true; - return this; - } - - @Override - public DefaultIssueBuilder atLine(int line) { - Preconditions.checkState(this.path != null && this.path instanceof InputFile, "atLine should be called after onFile"); - this.line = line; - return this; - } - - @Override - public DefaultIssueBuilder effortToFix(@Nullable Double effortToFix) { - this.effortToFix = effortToFix; - return this; - } - - @Override - public DefaultIssueBuilder message(String message) { - this.message = message; - return this; - } - - @Override - public IssueBuilder severity(@Nullable String severity) { - Preconditions.checkState(severity == null || Severity.ALL.contains(severity), "Invalid severity: " + severity); - this.severity = severity; - return this; - } - - /** - * For testing only. - */ - public DefaultIssueBuilder withKey(String key) { - this.key = key; - return this; - } - - @Override - public DefaultIssue build() { - DefaultIssue result = new DefaultIssue(this); - reset(); - return result; - } - - private void reset() { - key = null; - onProject = false; - path = null; - ruleKey = null; - message = null; - line = null; - effortToFix = null; - severity = null; - } - -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/Measure.java index 2ce2cf98361..86a0abdffad 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/Measure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/Measure.java @@ -19,7 +19,6 @@ */ package org.sonar.api.batch.sensor.measure; -import com.google.common.annotations.Beta; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.Metric; @@ -29,9 +28,8 @@ import java.io.Serializable; /** * Builder to create new Measure. - * @since 4.4 + * @since 5.0 */ -@Beta public interface Measure { /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java index f6b191355c7..fee7172f9ea 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java @@ -36,7 +36,7 @@ import java.io.Serializable; public class DefaultMeasure implements Measure { - private final SensorStorage> measureStorage; + private final SensorStorage storage; private boolean onProject = false; private InputFile file; private Metric metric; @@ -44,11 +44,11 @@ public class DefaultMeasure implements Measure { private boolean saved = false; public DefaultMeasure() { - this.measureStorage = null; + this.storage = null; } - public DefaultMeasure(@Nullable SensorStorage> measureStorage) { - this.measureStorage = measureStorage; + public DefaultMeasure(@Nullable SensorStorage storage) { + this.storage = storage; } @Override @@ -86,14 +86,12 @@ public class DefaultMeasure implements Measure { @Override public void save() { - Preconditions.checkNotNull(this.measureStorage, "No persister on this object"); + Preconditions.checkNotNull(this.storage, "No persister on this object"); Preconditions.checkState(!saved, "This measure was already saved"); Preconditions.checkNotNull(this.value, "Measure value can't be null"); Preconditions.checkNotNull(this.metric, "Measure metric can't be null"); Preconditions.checkState(this.metric.valueType().equals(this.value.getClass()), "Measure value should be of type " + this.metric.valueType()); - if (measureStorage != null) { - measureStorage.store(this); - } + storage.store((Measure) this); this.saved = true; } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java index 6c0d12da82c..722f0948120 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java @@ -23,10 +23,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.SensorStorage; import org.sonar.api.rule.RuleKey; import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class DefaultIssueTest { @@ -35,58 +37,56 @@ public class DefaultIssueTest { @Test public void build_file_issue() { - Issue issue = new DefaultIssueBuilder() + SensorStorage storage = mock(SensorStorage.class); + DefaultIssue issue = new DefaultIssue(storage) .onFile(new DefaultInputFile("foo", "src/Foo.php")) .ruleKey(RuleKey.of("repo", "rule")) .atLine(1) .effortToFix(10.0) - .message("Wrong way!") - .build(); + .message("Wrong way!"); assertThat(issue.inputPath()).isEqualTo(new DefaultInputFile("foo", "src/Foo.php")); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); assertThat(issue.line()).isEqualTo(1); assertThat(issue.effortToFix()).isEqualTo(10.0); assertThat(issue.message()).isEqualTo("Wrong way!"); + + issue.save(); + + verify(storage).store(issue); } @Test public void build_project_issue() { - Issue issue = new DefaultIssueBuilder() + SensorStorage storage = mock(SensorStorage.class); + DefaultIssue issue = new DefaultIssue(storage) .onProject() .ruleKey(RuleKey.of("repo", "rule")) .effortToFix(10.0) - .message("Wrong way!") - .build(); + .message("Wrong way!"); assertThat(issue.inputPath()).isNull(); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); assertThat(issue.line()).isNull(); assertThat(issue.effortToFix()).isEqualTo(10.0); assertThat(issue.message()).isEqualTo("Wrong way!"); + + issue.save(); + + verify(storage).store(issue); } @Test public void not_allowed_to_call_onFile_and_onProject() { thrown.expect(IllegalStateException.class); thrown.expectMessage("onProject already called"); - new DefaultIssueBuilder() + new DefaultIssue() .onProject() .onFile(new DefaultInputFile("foo", "src/Foo.php")) .ruleKey(RuleKey.of("repo", "rule")) .atLine(1) .effortToFix(10.0) - .message("Wrong way!") - .build(); - } - - @Test - public void validate_severity() { - thrown.expect(IllegalStateException.class); - thrown.expectMessage("Invalid severity: FOO"); - new DefaultIssueBuilder() - .severity("FOO") - .build(); + .message("Wrong way!"); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasureTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasureTest.java index 7085ca90169..7fa5fbfc593 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasureTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasureTest.java @@ -37,8 +37,8 @@ public class DefaultMeasureTest { @Test public void build_file_measure() { - SensorStorage> persister = mock(SensorStorage.class); - DefaultMeasure newMeasure = new DefaultMeasure(persister) + SensorStorage storage = mock(SensorStorage.class); + DefaultMeasure newMeasure = new DefaultMeasure(storage) .forMetric(CoreMetrics.LINES) .onFile(new DefaultInputFile("foo", "src/Foo.php")) .withValue(3); @@ -49,13 +49,13 @@ public class DefaultMeasureTest { newMeasure.save(); - verify(persister).store(newMeasure); + verify(storage).store(newMeasure); } @Test public void build_project_measure() { - SensorStorage> persister = mock(SensorStorage.class); - DefaultMeasure newMeasure = new DefaultMeasure(persister) + SensorStorage storage = mock(SensorStorage.class); + DefaultMeasure newMeasure = new DefaultMeasure(storage) .forMetric(CoreMetrics.LINES) .onProject() .withValue(3); @@ -66,7 +66,7 @@ public class DefaultMeasureTest { newMeasure.save(); - verify(persister).store(newMeasure); + verify(storage).store(newMeasure); } @Test