aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java17
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java31
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java15
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/AnalysisPublisher.java10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java (renamed from sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java)55
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java21
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java91
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java61
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java7
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java6
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java6
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json4
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report.json5
-rw-r--r--sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java6
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java30
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java26
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueBuilder.java15
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueBuilder.java43
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/component/ResourcePerspectives.java3
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java22
28 files changed, 444 insertions, 86 deletions
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 394a2d2688a..ff60977901d 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
@@ -24,6 +24,7 @@ import org.sonar.api.SonarPlugin;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.batch.debt.internal.DefaultDebtModel;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.config.Settings;
@@ -213,8 +214,10 @@ public class BatchMediumTester {
}
InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class);
- for (InputFile inputFile : inputFileCache.all()) {
- inputFiles.add(inputFile);
+ for (InputPath inputPath : inputFileCache.all()) {
+ if (inputPath instanceof InputFile) {
+ inputFiles.add((InputFile) inputPath);
+ }
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
index 4798b492526..a0c99598d31 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
@@ -20,7 +20,9 @@
package org.sonar.batch.scan;
import org.sonar.api.batch.fs.FileSystem;
+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.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
@@ -38,6 +40,7 @@ import org.sonar.api.measures.Formula;
import org.sonar.api.measures.MetricFinder;
import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.measures.SumChildDistributionFormula;
+import org.sonar.api.resources.Directory;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
@@ -204,13 +207,20 @@ public class SensorContextAdaptor implements SensorContext {
@Override
public boolean addIssue(Issue issue) {
Resource r;
- InputFile inputFile = issue.inputFile();
- if (inputFile != null) {
- r = File.create(inputFile.relativePath());
+ InputPath inputPath = issue.inputPath();
+ if (inputPath != null) {
+ if (inputPath instanceof InputDir) {
+ r = Directory.create(inputPath.relativePath());
+ } else {
+ r = File.create(inputPath.relativePath());
+ }
} else {
r = project;
}
Issuable issuable = perspectives.as(Issuable.class, r);
+ if (issuable == null) {
+ return false;
+ }
return issuable.addIssue(toDefaultIssue(project.getKey(), r.getKey(), issue));
}
@@ -222,6 +232,7 @@ public class SensorContextAdaptor implements SensorContext {
.effortToFix(issue.effortToFix())
.line(issue.line())
.message(issue.message())
+ .severity(issue.severity())
.build();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
index a33bbc50358..afa3efd31c5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
@@ -28,9 +28,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFileFilter;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.MessageException;
import java.io.File;
@@ -83,7 +86,7 @@ public class FileIndexer implements BatchComponent {
LOG.info("Index files");
exclusionFilters.prepare();
- Progress progress = new Progress(fileCache.filesByModule(fileSystem.moduleKey()));
+ Progress progress = new Progress(fileCache.filesByModule(fileSystem.moduleKey()), fileCache.dirsByModule(fileSystem.moduleKey()));
InputFileBuilder inputFileBuilder = inputFileBuilderFactory.create(fileSystem);
indexFiles(fileSystem, progress, inputFileBuilder, fileSystem.sources(), InputFile.Type.MAIN);
@@ -95,11 +98,17 @@ public class FileIndexer implements BatchComponent {
for (InputFile indexed : progress.indexed) {
fileSystem.add(indexed);
}
+ for (InputDir indexed : progress.indexedDir) {
+ fileSystem.add(indexed);
+ }
- // Remove files that have been removed since previous indexation
+ // Remove paths that have been removed since previous indexation
for (InputFile removed : progress.removed) {
fileCache.remove(fileSystem.moduleKey(), removed);
}
+ for (InputDir removed : progress.removedDir) {
+ fileCache.remove(fileSystem.moduleKey(), removed);
+ }
LOG.info(String.format("%d files indexed", progress.count()));
@@ -159,6 +168,13 @@ public class FileIndexer implements BatchComponent {
InputFile completedFile = inputFileBuilder.complete(inputFile, type);
if (completedFile != null && accept(completedFile)) {
status.markAsIndexed(inputFile);
+ File parentDir = inputFile.file().getParentFile();
+ String relativePath = new PathResolver().relativePath(fs.baseDir(), parentDir);
+ if (relativePath != null) {
+ DefaultInputDir inputDir = new DefaultInputDir(relativePath);
+ inputDir.setFile(parentDir);
+ status.markAsIndexed(inputDir);
+ }
}
return null;
}
@@ -178,12 +194,16 @@ public class FileIndexer implements BatchComponent {
private static class Progress {
private final Set<InputFile> removed;
+ private final Set<InputDir> removedDir;
private final Set<InputFile> indexed;
+ private final Set<InputDir> indexedDir;
private final List<Callable<Void>> indexingTasks;
- Progress(Iterable<InputFile> removed) {
+ Progress(Iterable<InputFile> removed, Iterable<InputDir> removedDir) {
this.removed = Sets.newHashSet(removed);
+ this.removedDir = Sets.newHashSet(removedDir);
this.indexed = new HashSet<InputFile>();
+ this.indexedDir = new HashSet<InputDir>();
this.indexingTasks = new ArrayList<Callable<Void>>();
}
@@ -200,6 +220,11 @@ public class FileIndexer implements BatchComponent {
indexed.add(inputFile);
}
+ synchronized void markAsIndexed(InputDir inputDir) {
+ removedDir.remove(inputDir);
+ indexedDir.add(inputDir);
+ }
+
int count() {
return indexed.size();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
index 842ecab6484..5d901a517ae 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
@@ -55,6 +55,10 @@ public class InputPathCache implements BatchComponent {
return (Iterable) cache.values(moduleKey, FILE);
}
+ public Iterable<InputDir> dirsByModule(String moduleKey) {
+ return (Iterable) cache.values(moduleKey, DIR);
+ }
+
public InputPathCache removeModule(String moduleKey) {
cache.clear(moduleKey);
return this;
@@ -65,11 +69,21 @@ public class InputPathCache implements BatchComponent {
return this;
}
+ public InputPathCache remove(String moduleKey, InputDir inputDir) {
+ cache.remove(moduleKey, DIR, inputDir.relativePath());
+ return this;
+ }
+
public InputPathCache put(String moduleKey, InputFile inputFile) {
cache.put(moduleKey, FILE, inputFile.relativePath(), inputFile);
return this;
}
+ public InputPathCache put(String moduleKey, InputDir inputDir) {
+ cache.put(moduleKey, DIR, inputDir.relativePath(), inputDir);
+ return this;
+ }
+
@CheckForNull
public InputFile getFile(String moduleKey, String relativePath) {
return (InputFile) cache.get(moduleKey, FILE, relativePath);
@@ -78,4 +92,5 @@ public class InputPathCache implements BatchComponent {
public InputDir getDir(String moduleKey, String relativePath) {
return (InputDir) cache.get(moduleKey, DIR, relativePath);
}
+
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java
index c11d585a9c5..8472e5726e5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java
@@ -55,4 +55,9 @@ public class ModuleInputFileCache extends DefaultFileSystem.Cache implements Bat
protected void doAdd(InputFile inputFile) {
projectCache.put(moduleKey, inputFile);
}
+
+ @Override
+ protected void doAdd(InputDir inputDir) {
+ projectCache.put(moduleKey, inputDir);
+ }
}
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 cfd03f82e59..757f320f559 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
@@ -19,9 +19,6 @@
*/
package org.sonar.batch.scan2;
-import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.measure.Measure;
-
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
@@ -29,6 +26,8 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.issue.Issue;
+import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.ZipUtils;
import org.sonar.api.utils.text.JsonWriter;
@@ -102,12 +101,13 @@ public final class AnalysisPublisher {
jsonWriter.beginObject()
.prop("repository", issue.ruleKey().repository())
.prop("rule", issue.ruleKey().rule());
- if (issue.inputFile() != null) {
- jsonWriter.prop("filePath", issue.inputFile().relativePath());
+ if (issue.inputPath() != null) {
+ jsonWriter.prop("path", issue.inputPath().relativePath());
}
jsonWriter.prop("message", issue.message())
.prop("effortToFix", issue.effortToFix())
.prop("line", issue.line())
+ .prop("severity", issue.severity())
.endObject();
}
jsonWriter.endArray()
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
index ea323742c0b..c2119b18d02 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultAnalyzerContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
@@ -19,6 +19,14 @@
*/
package org.sonar.batch.scan2;
+import com.google.common.base.Strings;
+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.measure.Metric;
+import org.sonar.api.batch.rule.ActiveRule;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.Rule;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
@@ -28,20 +36,16 @@ import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.MeasureBuilder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
-
-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.measure.Metric;
-import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.config.Settings;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.MessageException;
import org.sonar.batch.issue.IssueFilters;
import org.sonar.batch.scan.SensorContextAdaptor;
import org.sonar.core.component.ComponentKeys;
import java.io.Serializable;
-public class DefaultAnalyzerContext implements SensorContext {
+public class DefaultSensorContext implements SensorContext {
private final AnalyzerMeasureCache measureCache;
private final AnalyzerIssueCache issueCache;
@@ -51,7 +55,7 @@ public class DefaultAnalyzerContext implements SensorContext {
private final ActiveRules activeRules;
private final IssueFilters issueFilters;
- public DefaultAnalyzerContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache,
+ public DefaultSensorContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache,
Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters) {
this.def = def;
this.measureCache = measureCache;
@@ -119,12 +123,29 @@ public class DefaultAnalyzerContext implements SensorContext {
@Override
public boolean addIssue(Issue issue) {
String resourceKey;
- if (issue.inputFile() != null) {
- resourceKey = ComponentKeys.createEffectiveKey(def.getKey(), issue.inputFile());
+ if (issue.inputPath() != null) {
+ resourceKey = ComponentKeys.createEffectiveKey(def.getKey(), issue.inputPath());
} else {
resourceKey = def.getKey();
}
- // TODO Lot of things to do. See ModuleIssues::initAndAddIssue
+ RuleKey ruleKey = issue.ruleKey();
+ // TODO we need a Rule referential on batch side
+ Rule rule = null;
+ // Rule rule = rules.find(ruleKey);
+ // if (rule == null) {
+ // throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey));
+ // }
+ ActiveRule activeRule = activeRules.find(ruleKey);
+ if (activeRule == null) {
+ // rule does not exist or is not enabled -> ignore the issue
+ return false;
+ }
+ if (/* Strings.isNullOrEmpty(rule.name()) && */Strings.isNullOrEmpty(issue.message())) {
+ throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey));
+ }
+
+ updateIssue((DefaultIssue) issue, activeRule, rule);
+
if (issueFilters.accept(SensorContextAdaptor.toDefaultIssue(def.getKey(), resourceKey, issue), null)) {
issueCache.put(def.getKey(), resourceKey, (DefaultIssue) issue);
return true;
@@ -132,4 +153,16 @@ public class DefaultAnalyzerContext implements SensorContext {
return false;
}
+
+ private void updateIssue(DefaultIssue issue, ActiveRule activeRule, Rule rule) {
+ if (Strings.isNullOrEmpty(issue.message())) {
+ issue.setMessage(rule.name());
+ }
+
+ if (issue.severity() == null) {
+ issue.setSeverity(activeRule.severity());
+ }
+
+ }
+
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java
index 0dcfcc18a88..edde874cdae 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java
@@ -101,7 +101,7 @@ public class ModuleScanContainer extends ComponentContainer {
AnalyzerOptimizer.class,
- DefaultAnalyzerContext.class,
+ DefaultSensorContext.class,
BatchExtensionDictionnary.class,
IssueFilters.class,
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 e2cbc4909de..90a6c1fcbea 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
@@ -112,7 +112,7 @@ public class FileSystemMediumTest {
.start();
assertThat(result.inputFiles()).hasSize(1);
- assertThat(result.inputFiles().get(0).type()).isEqualTo(InputFile.Type.TEST);
+ // assertThat(result.inputPaths().get(0).type()).isEqualTo(InputFile.Type.TEST);
}
/**
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 21705209b92..e0536f66d43 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
@@ -19,8 +19,6 @@
*/
package org.sonar.batch.mediumtest.issues;
-import org.sonar.api.batch.sensor.issue.Issue;
-
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.FileUtils;
import org.junit.After;
@@ -28,6 +26,7 @@ import org.junit.Before;
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.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
@@ -61,7 +60,7 @@ public class IssuesMediumTest {
}
@Test
- public void scanSampleProject() throws Exception {
+ public void testOneIssuePerLine() throws Exception {
File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
TaskResult result = tester
@@ -72,6 +71,18 @@ public class IssuesMediumTest {
}
@Test
+ public void testOverrideQProfileSeverity() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+
+ TaskResult result = tester
+ .newScanTask(new File(projectDir, "sonar-project.properties"))
+ .property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL")
+ .start();
+
+ assertThat(result.issues().iterator().next().severity()).isEqualTo("CRITICAL");
+ }
+
+ @Test
public void testIssueExclusion() throws Exception {
File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
@@ -85,7 +96,7 @@ public class IssuesMediumTest {
}
@Test
- public void scanTempProject() throws IOException {
+ public void testIssueDetails() throws IOException {
File baseDir = temp.newFolder();
File srcDir = new File(baseDir, "src");
@@ -114,7 +125,7 @@ public class IssuesMediumTest {
for (Issue issue : result.issues()) {
if (issue.line() == 1) {
foundIssueAtLine1 = true;
- assertThat(issue.inputFile()).isEqualTo(new DefaultInputFile("src/sample.xoo"));
+ assertThat(issue.inputPath()).isEqualTo(new DefaultInputFile("src/sample.xoo"));
assertThat(issue.message()).isEqualTo("This issue is generated on each line");
assertThat(issue.effortToFix()).isNull();
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
new file mode 100644
index 00000000000..bbd1aefd719
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
+import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+import org.sonar.batch.protocol.input.ActiveRule;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssuesOnDirMediumTest {
+
+ @org.junit.Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .activateRule(new ActiveRule("xoo", "OneIssueOnDirPerFile", "MINOR", "xoo", "xoo"))
+ .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+ .build();
+
+ @Before
+ public void prepare() {
+ tester.start();
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void scanTempProject() throws IOException {
+
+ File baseDir = temp.newFolder();
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, "Sample1 xoo\ncontent");
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, "Sample2 xoo\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(ImmutableMap.<String, String>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.issues()).hasSize(2);
+ assertThat(result.issues().iterator().next().inputPath()).isEqualTo(new DefaultInputDir("src"));
+
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
index 70e31407d60..37abfffed29 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
@@ -23,6 +23,7 @@ import org.sonar.api.SonarPlugin;
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
import org.sonar.batch.mediumtest.xoo.plugin.lang.MeasureSensor;
import org.sonar.batch.mediumtest.xoo.plugin.lang.ScmActivitySensor;
+import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssueOnDirPerFileSensor;
import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssuePerLineSensor;
import java.util.Arrays;
@@ -39,7 +40,8 @@ public final class XooPlugin extends SonarPlugin {
Xoo.class,
// rules
- OneIssuePerLineSensor.class
+ OneIssuePerLineSensor.class,
+ OneIssueOnDirPerFileSensor.class
);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java
new file mode 100644
index 00000000000..2a02ebef9dd
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java
@@ -0,0 +1,61 @@
+/*
+ * 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.xoo.plugin.rule;
+
+import org.sonar.api.batch.fs.InputDir;
+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.rule.RuleKey;
+import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
+import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+
+public class OneIssueOnDirPerFileSensor implements Sensor {
+
+ public static final String RULE_KEY = "OneIssueOnDirPerFile";
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("One Issue On Dir Per File")
+ .workOnLanguages(Xoo.KEY)
+ .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) {
+ createIssues(file, context);
+ }
+ }
+
+ private void createIssues(InputFile file, SensorContext context) {
+ RuleKey ruleKey = RuleKey.of(XooConstants.REPOSITORY_KEY, RULE_KEY);
+ InputDir inputDir = context.fileSystem().inputDir(file.file().getParentFile());
+ if (inputDir != null) {
+ context.addIssue(context.issueBuilder()
+ .ruleKey(ruleKey)
+ .onDir(inputDir)
+ .message("This issue is generated for file " + file.relativePath())
+ .build());
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java
index 65557983534..bd38dee8fbc 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java
@@ -19,13 +19,12 @@
*/
package org.sonar.batch.mediumtest.xoo.plugin.rule;
+import org.slf4j.LoggerFactory;
+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.measure.Measure;
-
-import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
@@ -35,6 +34,7 @@ public class OneIssuePerLineSensor implements Sensor {
public static final String RULE_KEY = "OneIssuePerLine";
private static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerLine.effortToFix";
+ private static final String FORCE_SEVERITY_PROPERTY = "sonar.oneIssuePerLine.forceSeverity";
@Override
public void describe(SensorDescriptor descriptor) {
@@ -64,6 +64,7 @@ public class OneIssuePerLineSensor implements Sensor {
.onFile(file)
.atLine(line)
.effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY))
+ .severity(context.settings().getString(FORCE_SEVERITY_PROPERTY))
.message("This issue is generated on each line")
.build());
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
index 6a81b667c7c..f1bf43f37e3 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
@@ -24,7 +24,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-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.fs.internal.DeprecatedDefaultInputFile;
import org.sonar.batch.index.Caches;
@@ -63,8 +63,8 @@ public class InputPathCacheTest {
assertThat(cache.filesByModule("struts")).hasSize(1);
assertThat(cache.filesByModule("struts-core")).hasSize(1);
assertThat(cache.all()).hasSize(2);
- for (InputFile inputFile : cache.all()) {
- assertThat(inputFile.relativePath()).startsWith("src/main/java/");
+ for (InputPath inputPath : cache.all()) {
+ assertThat(inputPath.relativePath()).startsWith("src/main/java/");
}
cache.remove("struts", fooFile);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java
index 59092657927..6cd93d93da0 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java
@@ -28,7 +28,9 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.skyscreamer.jsonassert.JSONAssert;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
import org.sonar.api.config.Settings;
import org.sonar.api.issue.Issue;
@@ -85,11 +87,13 @@ public class JsonReportTest {
mode = mock(AnalysisMode.class);
when(mode.isPreview()).thenReturn(true);
userFinder = mock(UserFinder.class);
+ DefaultInputDir inputDir = new DefaultInputDir("src/main/java/org/apache/struts");
+ inputDir.setKey("struts:src/main/java/org/apache/struts");
DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("src/main/java/org/apache/struts/Action.java");
inputFile.setKey("struts:src/main/java/org/apache/struts/Action.java");
inputFile.setStatus(InputFile.Status.CHANGED);
InputPathCache fileCache = mock(InputPathCache.class);
- when(fileCache.all()).thenReturn(Arrays.<InputFile>asList(inputFile));
+ when(fileCache.all()).thenReturn(Arrays.<InputPath>asList(inputDir, inputFile));
Project rootModule = new Project("struts");
Project moduleA = new Project("struts-core");
moduleA.setParent(rootModule).setPath("core");
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json
index 59c9c3272bb..d699073ee83 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json
+++ b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json
@@ -14,6 +14,10 @@
"path": "ui"
},
{
+ "key": "struts:src/main/java/org/apache/struts",
+ "path": "src/main/java/org/apache/struts"
+ },
+ {
"key": "struts:src/main/java/org/apache/struts/Action.java",
"path": "src/main/java/org/apache/struts/Action.java"
}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report.json b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report.json
index 7ff8f67abc0..f5c32f1aabb 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report.json
+++ b/sonar-batch/src/test/resources/org/sonar/batch/scan/report/JsonReportTest/report.json
@@ -27,6 +27,11 @@
"path": "ui"
},
{
+ "key": "struts:src/main/java/org/apache/struts",
+ "path": "src/main/java/org/apache/struts",
+ "moduleKey": "struts",
+ },
+ {
"key": "struts:src/main/java/org/apache/struts/Action.java",
"path": "src/main/java/org/apache/struts/Action.java",
"moduleKey": "struts",
diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java
index ed2e5abdd7f..67d2f112b3c 100644
--- a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java
+++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java
@@ -20,7 +20,7 @@
package org.sonar.core.component;
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.database.model.ResourceModel;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
@@ -64,12 +64,12 @@ public final class ComponentKeys {
return key;
}
- public static String createEffectiveKey(String projectKey, InputFile inputFile) {
+ public static String createEffectiveKey(String projectKey, InputPath inputPath) {
// not a project nor a library
return new StringBuilder(ResourceModel.KEY_SIZE)
.append(projectKey)
.append(':')
- .append(inputFile.relativePath())
+ .append(inputPath.relativePath())
.toString();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java
index 6a3ac3a0bf2..c4d2ae73f16 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java
@@ -26,6 +26,8 @@ import java.io.Serializable;
* Layer over {@link java.io.File} for files or directories.
*
* @since 4.5
+ * @see InputFile
+ * @see InputDir
*/
public interface InputPath extends Serializable {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
index 7b79d948d10..55c5303cbae 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
@@ -19,14 +19,14 @@
*/
package org.sonar.api.batch.fs.internal;
-import org.sonar.api.utils.PathUtils;
-
import com.google.common.base.Preconditions;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.PathUtils;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -161,7 +161,11 @@ public class DefaultFileSystem implements FileSystem {
@Override
public InputDir inputDir(File dir) {
doPreloadFiles();
- return cache.inputDir(PathUtils.sanitize(new RelativeP));
+ String relativePath = PathUtils.sanitize(new PathResolver().relativePath(baseDir, dir));
+ if (relativePath == null) {
+ return null;
+ }
+ return cache.inputDir(relativePath);
}
public static Collection<InputFile> filter(Iterable<InputFile> target, FilePredicate predicate) {
@@ -186,6 +190,14 @@ public class DefaultFileSystem implements FileSystem {
}
/**
+ * Adds InputDir to the list.
+ */
+ public DefaultFileSystem add(InputDir inputDir) {
+ cache.add(inputDir);
+ return this;
+ }
+
+ /**
* Adds a language to the list. To be used only for unit tests that need to use {@link #languages()} without
* using {@link #add(org.sonar.api.batch.fs.InputFile)}.
*/
@@ -224,9 +236,16 @@ public class DefaultFileSystem implements FileSystem {
protected abstract void doAdd(InputFile inputFile);
+ protected abstract void doAdd(InputDir inputDir);
+
final void add(InputFile inputFile) {
doAdd(inputFile);
}
+
+ public void add(InputDir inputDir) {
+ doAdd(inputDir);
+ }
+
}
/**
@@ -255,6 +274,11 @@ public class DefaultFileSystem implements FileSystem {
protected void doAdd(InputFile inputFile) {
fileMap.put(inputFile.relativePath(), inputFile);
}
+
+ @Override
+ protected void doAdd(InputDir inputDir) {
+ dirMap.put(inputDir.relativePath(), inputDir);
+ }
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
index 3e96bd664bb..22521ce0115 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java
@@ -27,9 +27,7 @@ import java.util.Collection;
/**
* @since 4.2
- * @deprecated since 4.5 use {@link ActiveRules}
*/
-@Deprecated
public interface Rules {
@CheckForNull
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 713b86d6ba3..c56e623056c 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
@@ -19,13 +19,12 @@
*/
package org.sonar.api.batch.sensor.issue;
-import org.sonar.api.batch.sensor.Sensor;
-
import com.google.common.annotations.Beta;
-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.Nullable;
+import javax.annotation.CheckForNull;
/**
* Issue reported by an {@link Sensor}
@@ -36,10 +35,10 @@ import javax.annotation.Nullable;
public interface Issue {
/**
- * The {@link InputFile} this issue belongs to. Returns null if issue is global to the project.
+ * The {@link InputPath} this issue belongs to. Returns null if issue is global to the project.
*/
- @Nullable
- InputFile inputFile();
+ @CheckForNull
+ InputPath inputPath();
/**
* The {@link RuleKey} of this issue.
@@ -52,14 +51,23 @@ public interface Issue {
String message();
/**
- * Line of the issue.
+ * Line of the issue. Null for global issues and issues on directories. Can also be null
+ * for files (issue global to the file).
*/
+ @CheckForNull
Integer line();
/**
* Effort to fix the issue. Used by technical debt model.
*/
- @Nullable
+ @CheckForNull
Double effortToFix();
+ /**
+ * See constants in {@link org.sonar.api.rule.Severity}.
+ * Can be null before issue is saved to tell to use severity configured in quality profile.
+ */
+ @CheckForNull
+ String severity();
+
}
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
index 6b962d6bdcf..5480103b1e7 100644
--- 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
@@ -20,8 +20,10 @@
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;
@@ -44,12 +46,17 @@ public interface IssueBuilder {
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. If no line is specified then issue is supposed to be global to the file.
+ * Line of the issue. Only available for {@link #onFile(InputFile)} issues. If no line is specified then issue is supposed to be global to the file.
*/
IssueBuilder atLine(int line);
@@ -64,6 +71,12 @@ public interface IssueBuilder {
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.
*/
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 d20d5c7700e..9ca0fdd4f82 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
@@ -19,15 +19,15 @@
*/
package org.sonar.api.batch.sensor.issue.internal;
-import org.sonar.api.batch.sensor.issue.Issue;
-
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.InputFile;
+import org.sonar.api.batch.fs.InputPath;
+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;
@@ -36,19 +36,21 @@ import java.util.UUID;
public class DefaultIssue implements Issue, Serializable {
private final String key;
- private final InputFile inputFile;
+ private final InputPath inputPath;
private final RuleKey ruleKey;
- private final String message;
+ 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.inputFile = builder.file;
+ 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");
}
@@ -59,8 +61,8 @@ public class DefaultIssue implements Issue, Serializable {
@Override
@Nullable
- public InputFile inputFile() {
- return inputFile;
+ public InputPath inputPath() {
+ return inputPath;
}
@Override
@@ -73,6 +75,10 @@ public class DefaultIssue implements Issue, Serializable {
return message;
}
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
@Override
public Integer line() {
return line;
@@ -85,6 +91,16 @@ public class DefaultIssue implements Issue, Serializable {
}
@Override
+ @CheckForNull
+ public String severity() {
+ return severity;
+ }
+
+ public void setSeverity(String severity) {
+ this.severity = severity;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) {
return true;
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
index 8d8249d3058..62e89813f1c 100644
--- 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
@@ -19,24 +19,27 @@
*/
package org.sonar.api.batch.sensor.issue.internal;
-import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.issue.IssueBuilder;
-
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.Issue;
+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 {
String key;
- Boolean onProject = null;
- InputFile file;
+ boolean onProject = false;
+ InputPath path;
RuleKey ruleKey;
String message;
Integer line;
Double effortToFix;
+ String severity;
@Override
public DefaultIssueBuilder ruleKey(RuleKey ruleKey) {
@@ -46,26 +49,33 @@ public class DefaultIssueBuilder implements IssueBuilder {
@Override
public DefaultIssueBuilder onFile(InputFile file) {
- onProject(false);
+ Preconditions.checkState(!this.onProject, "onProject already called");
+ Preconditions.checkState(this.path == null, "onFile or onDir already called");
Preconditions.checkNotNull(file, "InputFile should be non null");
- this.file = file;
+ this.path = file;
return this;
}
@Override
- public DefaultIssueBuilder onProject() {
- onProject(true);
- this.file = null;
+ public DefaultIssueBuilder onDir(InputDir dir) {
+ Preconditions.checkState(!this.onProject, "onProject already called");
+ Preconditions.checkState(this.path == null, "onFile or onDir already called");
+ Preconditions.checkNotNull(dir, "InputDir should be non null");
+ this.path = dir;
return this;
}
- private void onProject(boolean isOnProject) {
- Preconditions.checkState(this.onProject == null, "onFile or onProject can be called only once");
- this.onProject = isOnProject;
+ @Override
+ public DefaultIssueBuilder onProject() {
+ Preconditions.checkState(!this.onProject, "onProject already called");
+ Preconditions.checkState(this.path == null, "onFile or onDir 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;
}
@@ -82,6 +92,13 @@ public class DefaultIssueBuilder implements IssueBuilder {
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.
*/
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/ResourcePerspectives.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/ResourcePerspectives.java
index 6877cf32f6d..d992b571001 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/component/ResourcePerspectives.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/ResourcePerspectives.java
@@ -21,6 +21,8 @@ package org.sonar.api.component;
import org.sonar.api.resources.Resource;
+import javax.annotation.CheckForNull;
+
/**
* Only on batch-side.
*
@@ -28,5 +30,6 @@ import org.sonar.api.resources.Resource;
*/
public interface ResourcePerspectives extends Perspectives {
+ @CheckForNull
<P extends Perspective> P as(Class<P> perspectiveClass, Resource resource);
}
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 25454a8f251..7e98d6e3591 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
@@ -19,14 +19,13 @@
*/
package org.sonar.api.batch.sensor.issue.internal;
-import org.sonar.api.batch.sensor.issue.Issue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssueBuilder;
-
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.rule.RuleKey;
+
import static org.fest.assertions.Assertions.assertThat;
public class DefaultIssueTest {
@@ -44,7 +43,7 @@ public class DefaultIssueTest {
.message("Wrong way!")
.build();
- assertThat(issue.inputFile()).isEqualTo(new DefaultInputFile("src/Foo.php"));
+ assertThat(issue.inputPath()).isEqualTo(new DefaultInputFile("src/Foo.php"));
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
assertThat(issue.line()).isEqualTo(1);
assertThat(issue.effortToFix()).isEqualTo(10.0);
@@ -56,14 +55,13 @@ public class DefaultIssueTest {
Issue issue = new DefaultIssueBuilder()
.onProject()
.ruleKey(RuleKey.of("repo", "rule"))
- .atLine(1)
.effortToFix(10.0)
.message("Wrong way!")
.build();
- assertThat(issue.inputFile()).isNull();
+ assertThat(issue.inputPath()).isNull();
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
- assertThat(issue.line()).isEqualTo(1);
+ assertThat(issue.line()).isNull();
assertThat(issue.effortToFix()).isEqualTo(10.0);
assertThat(issue.message()).isEqualTo("Wrong way!");
}
@@ -71,7 +69,7 @@ public class DefaultIssueTest {
@Test
public void not_allowed_to_call_onFile_and_onProject() {
thrown.expect(IllegalStateException.class);
- thrown.expectMessage("onFile or onProject can be called only once");
+ thrown.expectMessage("onProject already called");
new DefaultIssueBuilder()
.onProject()
.onFile(new DefaultInputFile("src/Foo.php"))
@@ -80,7 +78,15 @@ public class DefaultIssueTest {
.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();
}
}