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 }