001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 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     */
020    package org.sonar.api.rules;
021    
022    import org.apache.commons.lang.builder.EqualsBuilder;
023    import org.apache.commons.lang.builder.HashCodeBuilder;
024    import org.apache.commons.lang.builder.ReflectionToStringBuilder;
025    import org.sonar.api.resources.Resource;
026    import org.sonar.api.utils.Logs;
027    
028    import java.util.Date;
029    
030    /**
031     * A class that represents a violation. A violation happens when a resource does not respect a defined rule.
032     */
033    public class Violation {
034    
035      private Resource resource;
036      private Rule rule;
037      private String message;
038      private RulePriority severity;
039      private Integer lineId;
040      private Double cost;
041      private Date createdAt;
042      private boolean switchedOff=false;
043    
044      /**
045       * Creates of a violation from a rule. Will need to define the resource later on
046       * 
047       * @deprecated since 2.3. Use the factory method create()
048       */
049      @Deprecated
050      public Violation(Rule rule) {
051        this.rule = rule;
052      }
053    
054      /**
055       * Creates a fully qualified violation
056       * 
057       * @param rule
058       *          the rule that has been violated
059       * @param resource
060       *          the resource the violation should be attached to
061       * @deprecated since 2.3. Use the factory method create()
062       */
063      @Deprecated
064      public Violation(Rule rule, Resource resource) {
065        this.resource = resource;
066        this.rule = rule;
067      }
068    
069      public Resource getResource() {
070        return resource;
071      }
072    
073      /**
074       * Sets the resource the violation applies to
075       * 
076       * @return the current object
077       */
078      public Violation setResource(Resource resource) {
079        this.resource = resource;
080        return this;
081      }
082    
083      public Rule getRule() {
084        return rule;
085      }
086    
087      /**
088       * Sets the rule violated
089       * 
090       * @return the current object
091       */
092      public Violation setRule(Rule rule) {
093        this.rule = rule;
094        return this;
095      }
096    
097      public String getMessage() {
098        return message;
099      }
100    
101      /**
102       * Sets the violation message
103       * 
104       * @return the current object
105       */
106      public Violation setMessage(String message) {
107        this.message = message;
108        return this;
109      }
110    
111      /**
112       * @return line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
113       * @see #hasLineId()
114       */
115      public Integer getLineId() {
116        return lineId;
117      }
118    
119      /**
120       * Sets the violation line.
121       * 
122       * @param lineId line number (numeration starts from 1), or <code>null</code> if violation doesn't belong to concrete line
123       * @return the current object
124       */
125      public Violation setLineId(Integer lineId) {
126        if (lineId != null && lineId < 1) {
127          // TODO this normalization was added in 2.8, throw exception in future versions - see http://jira.codehaus.org/browse/SONAR-2386
128          Logs.INFO.warn("line must not be less than 1 - in future versions this will cause IllegalArgumentException");
129          this.lineId = null;
130        } else {
131          this.lineId = lineId;
132        }
133        return this;
134      }
135    
136      /**
137       * @return <code>true<code> if violation belongs to concrete line
138       * @since 2.8
139       */
140      public boolean hasLineId() {
141        return lineId != null;
142      }
143    
144      /**
145       * @since 2.5
146       */
147      public RulePriority getSeverity() {
148        return severity;
149      }
150    
151      /**
152       * For internal use only.
153       * 
154       * @since 2.5
155       */
156      public Violation setSeverity(RulePriority severity) {
157        this.severity = severity;
158        return this;
159      }
160    
161      /**
162       * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829
163       */
164      @Deprecated
165      public RulePriority getPriority() {
166        return severity;
167      }
168    
169      /**
170       * For internal use only
171       * 
172       * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829
173       */
174      @Deprecated
175      public Violation setPriority(RulePriority priority) {
176        this.severity = priority;
177        return this;
178      }
179    
180      /**
181       * @see #setCost(Double)
182       * @since 2.4
183       */
184      public Double getCost() {
185        return cost;
186      }
187    
188      /**
189       * The cost to fix a violation can't be precisely computed without this information. Let's take the following example : a rule forbids to
190       * have methods whose complexity is greater than 10. Without this field "cost", the same violation is created with a method whose
191       * 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
192       * fix the method whose complexity is 15, and 3h5mn is required to fix the method whose complexity is 100.
193       * 
194       * @since 2.4
195       */
196      public Violation setCost(Double d) {
197        if (d == null || d >= 0) {
198          this.cost = d;
199          return this;
200        } else {
201          throw new IllegalArgumentException("Cost to fix violation can't be negative or NaN");
202        }
203      }
204    
205      /**
206       * @since 2.5
207       */
208      public Date getCreatedAt() {
209        return createdAt;
210      }
211    
212      /**
213       * For internal use only
214       * 
215       * @since 2.5
216       */
217      public Violation setCreatedAt(Date createdAt) {
218        this.createdAt = createdAt;
219        return this;
220      }
221    
222      /**
223       * Switches off the current violation. This is a kind of "mute", which means the violation exists but won't be counted as an active
224       * violation (and thus, won't be counted in the total number of violations). It's usually used for false-positives.
225       *
226       * The extensions which call this method must be executed
227       * 
228       * @since 2.8
229       * @param b
230       *          if true, the violation is considered OFF
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       * @return true if the violation has been switched off
242       */
243      public boolean isSwitchedOff() {
244        return switchedOff;
245      }
246    
247      @Override
248      public boolean equals(Object obj) {
249        if (!(obj instanceof Violation)) {
250          return false;
251        }
252        if (this == obj) {
253          return true;
254        }
255        Violation other = (Violation) obj;
256        return new EqualsBuilder().append(rule, other.getRule()).append(resource, other.getResource()).isEquals();
257      }
258    
259      @Override
260      public int hashCode() {
261        return new HashCodeBuilder(17, 37).append(getRule()).append(getResource()).toHashCode();
262      }
263    
264      @Override
265      public String toString() {
266        return ReflectionToStringBuilder.toString(this);
267      }
268    
269      public static Violation create(ActiveRule activeRule, Resource resource) {
270        return new Violation(activeRule.getRule()).setResource(resource);
271      }
272    
273      public static Violation create(Rule rule, Resource resource) {
274        return new Violation(rule).setResource(resource);
275      }
276    
277    }