001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.api.rules;
021
022import org.apache.commons.lang.builder.ReflectionToStringBuilder;
023import org.sonar.api.resources.Resource;
024import org.sonar.api.utils.Logs;
025
026import java.util.Date;
027
028/**
029 * A class that represents a violation. A violation happens when a resource does not respect a defined rule.
030 */
031public class Violation {
032
033  private Resource resource;
034  private Rule rule;
035  private String message;
036  private RulePriority severity;
037  private Integer lineId;
038  private Double cost;
039  private Date createdAt;
040  private boolean switchedOff = false;
041  private String checksum;
042  private boolean isNew = false;
043  private boolean isManual = false;
044  private Integer permanentId;
045  private Integer personId;
046
047  /**
048   * Creates of a violation from a rule. Will need to define the resource later on
049   *
050   * @deprecated since 2.3. Use the factory method create()
051   */
052  @Deprecated
053  public Violation(Rule rule) {
054    this.rule = rule;
055  }
056
057  /**
058   * Creates a fully qualified violation
059   *
060   * @param rule     the rule that has been violated
061   * @param resource the resource the violation should be attached to
062   * @deprecated since 2.3. Use the factory method create()
063   */
064  @Deprecated
065  public Violation(Rule rule, Resource resource) {
066    this.resource = resource;
067    this.rule = rule;
068  }
069
070  public Resource getResource() {
071    return resource;
072  }
073
074  /**
075   * Sets the resource the violation applies to
076   *
077   * @return the current object
078   */
079  public Violation setResource(Resource resource) {
080    this.resource = resource;
081    return this;
082  }
083
084  public Rule getRule() {
085    return rule;
086  }
087
088  /**
089   * Sets the rule violated
090   *
091   * @return the current object
092   */
093  public Violation setRule(Rule rule) {
094    this.rule = rule;
095    return this;
096  }
097
098  public String getMessage() {
099    return message;
100  }
101
102  /**
103   * Sets the violation message
104   *
105   * @return the current object
106   */
107  public Violation setMessage(String message) {
108    this.message = message;
109    return this;
110  }
111
112  /**
113   * @return line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
114   * @see #hasLineId()
115   */
116  public Integer getLineId() {
117    return lineId;
118  }
119
120  /**
121   * Sets the violation line.
122   *
123   * @param lineId line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
124   * @return the current object
125   */
126  public Violation setLineId(Integer lineId) {
127    if (lineId != null && lineId < 1) {
128      // TODO this normalization was added in 2.8, throw exception in future versions - see http://jira.codehaus.org/browse/SONAR-2386
129      Logs.INFO.warn("line must not be less than 1 - in future versions this will cause IllegalArgumentException");
130      this.lineId = null;
131    } else {
132      this.lineId = lineId;
133    }
134    return this;
135  }
136
137  /**
138   * @return <code>true<code> if violation belongs to concrete line
139   * @since 2.8
140   */
141  public boolean hasLineId() {
142    return lineId != null;
143  }
144
145  /**
146   * @since 2.5
147   */
148  public RulePriority getSeverity() {
149    return severity;
150  }
151
152  /**
153   * For internal use only.
154   *
155   * @since 2.5
156   */
157  public Violation setSeverity(RulePriority severity) {
158    this.severity = severity;
159    return this;
160  }
161
162  /**
163   * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829
164   */
165  @Deprecated
166  public RulePriority getPriority() {
167    return severity;
168  }
169
170  /**
171   * For internal use only
172   *
173   * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829
174   */
175  @Deprecated
176  public Violation setPriority(RulePriority priority) {
177    this.severity = priority;
178    return this;
179  }
180
181  /**
182   * @see #setCost(Double)
183   * @since 2.4
184   */
185  public Double getCost() {
186    return cost;
187  }
188
189  /**
190   * The cost to fix a violation can't be precisely computed without this information. Let's take the following example : a rule forbids to
191   * have methods whose complexity is greater than 10. Without this field "cost", the same violation is created with a method whose
192   * 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
193   * fix the method whose complexity is 15, and 3h5mn is required to fix the method whose complexity is 100.
194   *
195   * @since 2.4
196   */
197  public Violation setCost(Double d) {
198    if (d == null || d >= 0) {
199      this.cost = d;
200      return this;
201    } else {
202      throw new IllegalArgumentException("Cost to fix violation can't be negative or NaN");
203    }
204  }
205
206  /**
207   * @since 2.5
208   */
209  public Date getCreatedAt() {
210    return createdAt;
211  }
212
213  /**
214   * For internal use only
215   *
216   * @since 2.5
217   */
218  public Violation setCreatedAt(Date createdAt) {
219    this.createdAt = createdAt;
220    return this;
221  }
222
223  /**
224   * Switches off the current violation. This is a kind of "mute", which means the violation exists but won't be counted as an active
225   * violation (and thus, won't be counted in the total number of violations). It's usually used for false-positives.
226   * <p/>
227   * The extensions which call this method must be executed
228   *
229   * @param b if true, the violation is considered OFF
230   * @since 2.8
231   */
232  public Violation setSwitchedOff(boolean b) {
233    this.switchedOff = b;
234    return this;
235  }
236
237  /**
238   * Tells whether this violation is ON or OFF.
239   *
240   * @since 2.8
241   */
242  public boolean isSwitchedOff() {
243    return switchedOff;
244  }
245
246  /**
247   * Checksum is available in decorators executed after the barrier {@link org.sonar.api.batch.DecoratorBarriers#END_OF_VIOLATION_TRACKING}
248   */
249  public String getChecksum() {
250    return checksum;
251  }
252
253  /**
254   * For internal use only. Checksum is automatically set by Sonar. Plugins must not call this method.
255   */
256  public Violation setChecksum(String s) {
257    this.checksum = s;
258    return this;
259  }
260
261  /**
262   * A violation is considered as "new" if it has been created after the reference analysis
263   * (the "previous" analysis).
264   * This method must be used only by post-jobs and decorators depending on the barrier
265   * {@link org.sonar.api.batch.DecoratorBarriers#END_OF_VIOLATION_TRACKING}
266   *
267   * @since 2.9
268   */
269  public boolean isNew() {
270    return isNew;
271  }
272
273  /**
274   * For internal use only. MUST NOT BE SET FROM PLUGINS.
275   *
276   * @since 2.9
277   */
278  public Violation setNew(boolean b) {
279    isNew = b;
280    return this;
281  }
282
283  /**
284   * @since 2.13
285   */
286  public boolean isManual() {
287    return isManual;
288  }
289
290  /**
291   * For internal use only. MUST NOT BE SET FROM PLUGINS.
292   *
293   * @since 2.13
294   */
295  public Violation setManual(boolean b) {
296    isManual = b;
297    return this;
298  }
299
300  /**
301   * For internal use only. MUST NOT BE SET FROM PLUGINS.
302   *
303   * @since 2.13
304   */
305  public Integer getPermanentId() {
306    return permanentId;
307  }
308
309  /**
310   * For internal use only. MUST NOT BE SET FROM PLUGINS.
311   *
312   * @since 2.13
313   */
314  public Violation setPermanentId(Integer i) {
315    this.permanentId = i;
316    return this;
317  }
318
319  /**
320   * @since 2.13
321   */
322  public Integer getPersonId() {
323    return personId;
324  }
325
326  /**
327   * For internal use only.
328   * 
329   * @since 2.13
330   */
331  public Violation setPersonId(Integer i) {
332    this.personId = i;
333    return this;
334  }
335
336  @Override
337  public String toString() {
338    return ReflectionToStringBuilder.toString(this);
339  }
340
341  public static Violation create(ActiveRule activeRule, Resource resource) {
342    return new Violation(activeRule.getRule()).setResource(resource);
343  }
344
345  public static Violation create(Rule rule, Resource resource) {
346    return new Violation(rule).setResource(resource);
347  }
348
349}