aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java23
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/deprecated/decorator/DefaultDecoratorContext.java15
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java40
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java37
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java3
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java35
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java20
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java27
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java16
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rules.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java351
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/rules/ViolationTest.java73
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/test/IsViolation.java69
13 files changed, 702 insertions, 11 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
index b22660d590f..2a70c13ac9d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
@@ -37,6 +37,7 @@ import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.Metric;
import org.sonar.api.resources.*;
+import org.sonar.api.rules.Violation;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.sensor.DefaultSensorContext;
import org.sonar.batch.sensor.coverage.CoverageExclusions;
@@ -182,6 +183,28 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen
}
@Override
+ public void saveViolation(Violation violation, boolean force) {
+ if (violation.getResource() == null) {
+ violation.setResource(resourceOrProject(violation.getResource()));
+ }
+ index.addViolation(violation, force);
+ }
+
+ @Override
+ public void saveViolation(Violation violation) {
+ saveViolation(violation, false);
+ }
+
+ @Override
+ public void saveViolations(Collection<Violation> violations) {
+ if (violations != null) {
+ for (Violation violation : violations) {
+ saveViolation(violation);
+ }
+ }
+ }
+
+ @Override
public Dependency saveDependency(Dependency dependency) {
return index.addDependency(dependency);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/decorator/DefaultDecoratorContext.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/decorator/DefaultDecoratorContext.java
index 0b63501eb80..47d75f4434d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/deprecated/decorator/DefaultDecoratorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/decorator/DefaultDecoratorContext.java
@@ -29,6 +29,7 @@ import org.sonar.api.design.Dependency;
import org.sonar.api.measures.*;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.duplication.DuplicationUtils;
@@ -213,4 +214,18 @@ public class DefaultDecoratorContext implements DecoratorContext {
public Collection<Dependency> getOutgoingDependencies() {
return sonarIndex.getOutgoingEdges(resource);
}
+
+ @Override
+ public DefaultDecoratorContext saveViolation(Violation violation, boolean force) {
+ if (violation.getResource() == null) {
+ violation.setResource(resource);
+ }
+ sonarIndex.addViolation(violation, force);
+ return this;
+ }
+
+ @Override
+ public DefaultDecoratorContext saveViolation(Violation violation) {
+ return saveViolation(violation, false);
+ }
}
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 7a12b86eaab..33f6246ea8c 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
@@ -38,6 +38,8 @@ import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.MeasuresFilters;
import org.sonar.api.resources.*;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.Violation;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.ProjectTree;
@@ -359,6 +361,44 @@ public class DefaultIndex extends SonarIndex {
return result;
}
+ //
+ //
+ //
+ // VIOLATIONS
+ //
+ //
+ //
+
+ @Override
+ public void addViolation(Violation violation, boolean force) {
+ Resource resource = violation.getResource();
+ if (resource == null) {
+ violation.setResource(currentProject);
+ } else if (!Scopes.isHigherThanOrEquals(resource, Scopes.FILE)) {
+ throw new IllegalArgumentException("Violations are only supported on files, directories and project");
+ }
+
+ Rule rule = violation.getRule();
+ if (rule == null) {
+ LOG.warn("Rule is null. Ignoring violation {}", violation);
+ return;
+ }
+
+ Bucket bucket = getBucket(resource);
+ if (bucket == null) {
+ LOG.warn("Resource is not indexed. Ignoring violation {}", violation);
+ return;
+ }
+
+ // keep a limitation (bug?) of deprecated violations api : severity is always
+ // set by sonar. The severity set by plugins is overridden.
+ // This is not the case with issue api.
+ violation.setSeverity(null);
+
+ violation.setResource(bucket.getResource());
+ moduleIssues.initAndAddViolation(violation);
+ }
+
@Override
public String getSource(Resource reference) {
Resource resource = getResource(reference);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
index 94938954ebf..64f7e8d2446 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
@@ -30,8 +30,10 @@ import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Violation;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.MessageException;
+import org.sonar.core.issue.DefaultIssueBuilder;
import javax.annotation.Nullable;
@@ -46,7 +48,7 @@ public class ModuleIssues {
private final Project project;
private final IssueFilters filters;
- public ModuleIssues(ActiveRules activeRules, @Nullable Rules rules, IssueCache cache, Project project, IssueFilters filters) {
+ public ModuleIssues(ActiveRules activeRules, @Nullable Rules rules, IssueCache cache, @Nullable Project project, IssueFilters filters) {
this.activeRules = activeRules;
this.rules = rules;
this.cache = cache;
@@ -54,10 +56,35 @@ public class ModuleIssues {
this.filters = filters;
}
- public ModuleIssues(ActiveRules activeRules, IssueCache cache, Project project, IssueFilters filters) {
+ public ModuleIssues(ActiveRules activeRules, IssueCache cache, @Nullable Project project, IssueFilters filters) {
this(activeRules, null, cache, project, filters);
}
+ /**
+ * Used by scan2
+ */
+ public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, IssueFilters filters) {
+ this(activeRules, rules, cache, null, filters);
+ }
+
+ public boolean initAndAddViolation(Violation violation) {
+ DefaultIssue issue = newIssue(violation);
+ return initAndAddIssue(issue);
+ }
+
+ private DefaultIssue newIssue(Violation violation) {
+ return new DefaultIssueBuilder()
+ .componentKey(violation.getResource().getEffectiveKey())
+ // Project can be null but Violation not used by scan2
+ .projectKey(project.getRoot().getEffectiveKey())
+ .ruleKey(RuleKey.of(violation.getRule().getRepositoryKey(), violation.getRule().getKey()))
+ .effortToFix(violation.getCost())
+ .line(violation.getLineId())
+ .message(violation.getMessage())
+ .severity(violation.getSeverity() != null ? violation.getSeverity().name() : null)
+ .build();
+ }
+
public boolean initAndAddIssue(DefaultIssue issue) {
RuleKey ruleKey = issue.ruleKey();
Rule rule = null;
@@ -92,8 +119,10 @@ public class ModuleIssues {
if (Strings.isNullOrEmpty(issue.message())) {
issue.setMessage(((DefaultActiveRule) activeRule).name());
}
- issue.setCreationDate(project.getAnalysisDate());
- issue.setUpdateDate(project.getAnalysisDate());
+ if (project != null) {
+ issue.setCreationDate(project.getAnalysisDate());
+ issue.setUpdateDate(project.getAnalysisDate());
+ }
if (issue.severity() == null) {
issue.setSeverity(activeRule.severity());
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
index 1895e6d103b..c886d93bf51 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
@@ -23,7 +23,6 @@ package org.sonar.batch.rule;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.batch.RequiresDB;
import org.sonar.api.batch.debt.DebtCharacteristic;
import org.sonar.api.batch.debt.DebtModel;
import org.sonar.api.batch.debt.DebtRemediationFunction;
@@ -40,13 +39,11 @@ import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleParamDto;
import javax.annotation.Nullable;
-
import java.util.List;
/**
* Loads all enabled and non manual rules
*/
-@RequiresDB
public class RulesProvider extends ProviderAdapter {
private Rules singleton = null;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
index 6b33dab0611..7ffa9905721 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -30,9 +30,13 @@ import org.sonar.api.batch.debt.DebtRemediationFunction;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.RulesBuilder;
import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.Violation;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.MessageException;
@@ -41,6 +45,7 @@ import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -196,6 +201,36 @@ public class ModuleIssuesTest {
}
@Test
+ public void add_deprecated_violation() throws Exception {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
+ initModuleIssues();
+
+ org.sonar.api.rules.Rule rule = org.sonar.api.rules.Rule.create("squid", "AvoidCycle", "Avoid Cycle");
+ Resource resource = File.create("org/struts/Action.java").setEffectiveKey("struts:src/org/struts/Action.java");
+ Violation violation = new Violation(rule, resource);
+ violation.setLineId(42);
+ violation.setSeverity(RulePriority.CRITICAL);
+ violation.setMessage("the message");
+
+ when(filters.accept(any(DefaultIssue.class))).thenReturn(true);
+
+ boolean added = moduleIssues.initAndAddViolation(violation);
+ assertThat(added).isTrue();
+
+ ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
+ verify(cache).put(argument.capture());
+ DefaultIssue issue = argument.getValue();
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ assertThat(issue.line()).isEqualTo(42);
+ assertThat(issue.message()).isEqualTo("the message");
+ assertThat(issue.key()).isNotEmpty();
+ assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle");
+ assertThat(issue.componentKey()).isEqualTo("struts:src/org/struts/Action.java");
+ assertThat(issue.projectKey()).isEqualTo("org.apache:struts-core");
+ }
+
+ @Test
public void filter_issue() throws Exception {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java
index ae7f9dd3069..018d22cc0a0 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java
@@ -25,6 +25,7 @@ import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
import java.util.Collection;
import java.util.List;
@@ -96,4 +97,23 @@ public interface DecoratorContext {
Collection<Dependency> getOutgoingDependencies();
+ // RULES
+
+ /**
+ * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION.
+ *
+ * @since 2.5
+ * @param force allows to force creation of violation even if it was suppressed by {@link org.sonar.api.rules.ViolationFilter}
+ * @deprecated in 3.6, replaced by {@link org.sonar.api.issue.Issuable}
+ */
+ @Deprecated
+ DecoratorContext saveViolation(Violation violation, boolean force);
+
+ /**
+ * Save a coding rule violation. The decorator which calls this method must be depended upon BatchBarriers.END_OF_VIOLATIONS_GENERATION.
+ * @deprecated in 3.6, replaced by {@link org.sonar.api.issue.Issuable}
+ */
+ @Deprecated
+ DecoratorContext saveViolation(Violation violation);
+
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
index 28004e4b239..f5b73a25722 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
@@ -26,6 +26,7 @@ import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
import javax.annotation.CheckForNull;
@@ -162,6 +163,32 @@ public interface SensorContext extends org.sonar.api.batch.sensor.SensorContext
*/
Measure saveMeasure(Resource resource, Measure measure);
+ // ----------- RULE VIOLATIONS --------------
+
+ /**
+ * Save a coding rule violation.
+ *
+ * @param force allows to force creation of violation even if it was supressed by {@link org.sonar.api.rules.ViolationFilter}
+ * @since 2.5
+ * @deprecated in 3.6, replaced by {@link org.sonar.api.issue.Issuable}
+ */
+ @Deprecated
+ void saveViolation(Violation violation, boolean force);
+
+ /**
+ * Save a coding rule violation.
+ * @deprecated in 3.6, replaced by {@link org.sonar.api.issue.Issuable}
+ */
+ @Deprecated
+ void saveViolation(Violation violation);
+
+ /**
+ * Saves a list of violations.
+ * @deprecated in 3.6, replaced by {@link org.sonar.api.issue.Issuable}
+ */
+ @Deprecated
+ void saveViolations(Collection<Violation> violations);
+
// ----------- DEPENDENCIES BETWEEN RESOURCES --------------
Dependency saveDependency(Dependency dependency);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
index 6384ef65edc..5032c68afcc 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
@@ -25,6 +25,7 @@ import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
import org.sonar.graph.DirectedGraphAccessor;
import javax.annotation.CheckForNull;
@@ -119,6 +120,21 @@ public abstract class SonarIndex implements DirectedGraphAccessor<Resource, Depe
public abstract <M> M getMeasures(Resource resource, MeasuresFilter<M> filter);
/**
+ * @since 2.5
+ * @deprecated in 3.6
+ */
+ @Deprecated
+ public abstract void addViolation(Violation violation, boolean force);
+
+ /**
+ * @deprecated in 3.6
+ */
+ @Deprecated
+ public final void addViolation(Violation violation) {
+ addViolation(violation, false);
+ }
+
+ /**
* Warning: the resource is automatically indexed for backward-compatibility, but it should be explictly
* indexed before. Next versions will deactivate this automatic indexation.
*/
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 7bfdea2c835..7690bd56c94 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
@@ -20,7 +20,6 @@
package org.sonar.api.batch.rule;
import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.RequiresDB;
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
@@ -32,10 +31,7 @@ import java.util.Collection;
* be extended by plugins.
*
* @since 4.2
- * @deprecated since 5.2 no more possible to query any rule on batch side. Use {@link ActiveRules} to get active rules.
*/
-@Deprecated
-@RequiresDB
public interface Rules extends BatchComponent {
/**
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java
new file mode 100644
index 00000000000..7f19e8214cd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java
@@ -0,0 +1,351 @@
+/*
+ * 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.rules;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.log.Loggers;
+
+import java.util.Date;
+
+/**
+ * A class that represents a violation. A violation happens when a resource does not respect a defined rule.
+ * @deprecated in 3.6. Replaced by {@link org.sonar.api.issue.Issue}.
+ */
+@Deprecated
+public class Violation {
+
+ private Resource resource;
+ private Rule rule;
+ private String message;
+ private RulePriority severity;
+ private Integer lineId;
+ private Double cost;
+ private Date createdAt;
+ private boolean switchedOff = false;
+ private String checksum;
+ private boolean isNew = false;
+ private boolean isManual = false;
+ private Integer permanentId;
+ private Integer personId;
+
+ /**
+ * Creates of a violation from a rule. Will need to define the resource later on
+ *
+ * @deprecated since 2.3. Use the factory method {@link #create(ActiveRule, Resource)}
+ */
+ @Deprecated
+ public Violation(Rule rule) {
+ this.rule = rule;
+ }
+
+ /**
+ * Creates a fully qualified violation
+ *
+ * @param rule the rule that has been violated
+ * @param resource the resource the violation should be attached to
+ * @deprecated since 2.3. Use the factory method create()
+ */
+ @Deprecated
+ public Violation(Rule rule, Resource resource) {
+ this.resource = resource;
+ this.rule = rule;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ /**
+ * Sets the resource the violation applies to
+ *
+ * @return the current object
+ */
+ public Violation setResource(Resource resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ /**
+ * Sets the rule violated
+ *
+ * @return the current object
+ */
+ public Violation setRule(Rule rule) {
+ this.rule = rule;
+ return this;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Sets the violation message
+ *
+ * @return the current object
+ */
+ public Violation setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * @return line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
+ * @see #hasLineId()
+ */
+ public Integer getLineId() {
+ return lineId;
+ }
+
+ /**
+ * Sets the violation line.
+ *
+ * @param lineId line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
+ * @return the current object
+ */
+ public Violation setLineId(Integer lineId) {
+ if (lineId != null && lineId < 1) {
+ // TODO this normalization was added in 2.8, throw exception in future versions - see http://jira.codehaus.org/browse/SONAR-2386
+ Loggers.get(getClass()).warn("line must not be less than 1 - in future versions this will cause IllegalArgumentException");
+ this.lineId = null;
+ } else {
+ this.lineId = lineId;
+ }
+ return this;
+ }
+
+ /**
+ * @return <code>true<code> if violation belongs to concrete line
+ * @since 2.8
+ */
+ public boolean hasLineId() {
+ return lineId != null;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public RulePriority getSeverity() {
+ return severity;
+ }
+
+ /**
+ * For internal use only.
+ *
+ * @since 2.5
+ */
+ public Violation setSeverity(RulePriority severity) {
+ this.severity = severity;
+ return this;
+ }
+
+ /**
+ * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829
+ */
+ @Deprecated
+ public RulePriority getPriority() {
+ return severity;
+ }
+
+ /**
+ * For internal use only
+ *
+ * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829
+ */
+ @Deprecated
+ public Violation setPriority(RulePriority priority) {
+ this.severity = priority;
+ return this;
+ }
+
+ /**
+ * @see #setCost(Double)
+ * @since 2.4
+ */
+ public Double getCost() {
+ return cost;
+ }
+
+ /**
+ * The cost to fix a violation can't be precisely computed without this information. Let's take the following example : a rule forbids to
+ * have methods whose complexity is greater than 10. Without this field "cost", the same violation is created with a method whose
+ * complexity is 15 and a method whose complexity is 100. If the cost to fix one point of complexity is 0.05h, then 15mn is necessary to
+ * fix the method whose complexity is 15, and 3h5mn is required to fix the method whose complexity is 100.
+ *
+ * @since 2.4
+ */
+ public Violation setCost(Double d) {
+ if (d == null || d >= 0) {
+ this.cost = d;
+ return this;
+ } else {
+ throw new IllegalArgumentException("Cost to fix violation can't be negative or NaN");
+ }
+ }
+
+ /**
+ * @since 2.5
+ */
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ /**
+ * For internal use only
+ *
+ * @since 2.5
+ */
+ public Violation setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ /**
+ * Switches off the current violation. This is a kind of "mute", which means the violation exists but won't be counted as an active
+ * violation (and thus, won't be counted in the total number of violations). It's usually used for false-positives.
+ * <p/>
+ * The extensions which call this method must be executed
+ *
+ * @param b if true, the violation is considered OFF
+ * @since 2.8
+ */
+ public Violation setSwitchedOff(boolean b) {
+ this.switchedOff = b;
+ return this;
+ }
+
+ /**
+ * Tells whether this violation is ON or OFF.
+ *
+ * @since 2.8
+ */
+ public boolean isSwitchedOff() {
+ return switchedOff;
+ }
+
+ /**
+ * Checksum is available in decorators executed after the barrier {@link org.sonar.api.batch.DecoratorBarriers#END_OF_VIOLATION_TRACKING}
+ */
+ public String getChecksum() {
+ return checksum;
+ }
+
+ /**
+ * For internal use only. Checksum is automatically set by Sonar. Plugins must not call this method.
+ */
+ public Violation setChecksum(String s) {
+ this.checksum = s;
+ return this;
+ }
+
+ /**
+ * A violation is considered as "new" if it has been created after the reference analysis
+ * (the "previous" analysis).
+ * This method must be used only by post-jobs and decorators depending on the barrier
+ * {@link org.sonar.api.batch.DecoratorBarriers#END_OF_VIOLATION_TRACKING}
+ *
+ * @since 2.9
+ */
+ public boolean isNew() {
+ return isNew;
+ }
+
+ /**
+ * For internal use only. MUST NOT BE SET FROM PLUGINS.
+ *
+ * @since 2.9
+ */
+ public Violation setNew(boolean b) {
+ isNew = b;
+ return this;
+ }
+
+ /**
+ * @since 2.13
+ */
+ public boolean isManual() {
+ return isManual;
+ }
+
+ /**
+ * For internal use only. MUST NOT BE SET FROM PLUGINS.
+ *
+ * @since 2.13
+ */
+ public Violation setManual(boolean b) {
+ isManual = b;
+ return this;
+ }
+
+ /**
+ * For internal use only. MUST NOT BE SET FROM PLUGINS.
+ *
+ * @since 2.13
+ */
+ public Integer getPermanentId() {
+ return permanentId;
+ }
+
+ /**
+ * For internal use only. MUST NOT BE SET FROM PLUGINS.
+ *
+ * @since 2.13
+ */
+ public Violation setPermanentId(Integer i) {
+ this.permanentId = i;
+ return this;
+ }
+
+ /**
+ * @since 2.13
+ */
+ public Integer getPersonId() {
+ return personId;
+ }
+
+ /**
+ * For internal use only.
+ *
+ * @since 2.13
+ */
+ public Violation setPersonId(Integer i) {
+ this.personId = i;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this);
+ }
+
+ public static Violation create(ActiveRule activeRule, Resource resource) {
+ return new Violation(activeRule.getRule()).setResource(resource);
+ }
+
+ public static Violation create(Rule rule, Resource resource) {
+ return new Violation(rule).setResource(resource);
+ }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rules/ViolationTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rules/ViolationTest.java
new file mode 100644
index 00000000000..029fcc9d9f6
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/rules/ViolationTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.rules;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class ViolationTest {
+ private Violation violation;
+
+ @Before
+ public void setUp() {
+ violation = Violation.create((Rule) null, null);
+ }
+
+ /**
+ * See http://jira.codehaus.org/browse/SONAR-2386
+ */
+ @Test
+ public void testLineIdContract() {
+ violation.setLineId(null);
+ assertThat(violation.hasLineId(), is(false));
+ assertThat(violation.getLineId(), nullValue());
+
+ violation.setLineId(0);
+ assertThat(violation.hasLineId(), is(false));
+ assertThat(violation.getLineId(), nullValue());
+
+ violation.setLineId(1);
+ assertThat(violation.hasLineId(), is(true));
+ assertThat(violation.getLineId(), is(1));
+ }
+
+ @Test
+ public void testCostContract() {
+ violation.setCost(null);
+ assertThat(violation.getCost(), nullValue());
+
+ violation.setCost(1.0);
+ assertThat(violation.getCost(), is(1.0));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCostContract_NaN() {
+ violation.setCost(Double.NaN);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCostContract_Negative() {
+ violation.setCost(-1.0);
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/IsViolation.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/IsViolation.java
new file mode 100644
index 00000000000..c8b9a6b9774
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/test/IsViolation.java
@@ -0,0 +1,69 @@
+/*
+ * 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.test;
+
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.Violation;
+
+public class IsViolation extends ArgumentMatcher<Violation> {
+
+ private Rule rule;
+ private String message;
+ private Resource resource;
+ private Integer lineId;
+
+ public IsViolation(Violation wanted) {
+ this.lineId = wanted.getLineId();
+ this.message = wanted.getMessage();
+ this.resource = wanted.getResource();
+ this.rule = wanted.getRule();
+ }
+
+ public IsViolation(Rule rule, String message, Resource resource, Integer lineId) {
+ this.rule = rule;
+ this.message = message;
+ this.resource = resource;
+ this.lineId = lineId;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ Violation violation = (Violation) o;
+ if (lineId != null && !lineId.equals(violation.getLineId())) {
+ return false;
+ }
+
+ if (message != null && !message.equals(violation.getMessage())) {
+ return false;
+ }
+
+ if (resource != null && !resource.equals(violation.getResource())) {
+ return false;
+ }
+
+ if (rule != null && !rule.equals(violation.getRule())) {
+ return false;
+ }
+
+ return true;
+ }
+}