diff options
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; + } +} |