001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020
021 package org.sonar.api.rules;
022
023 import com.google.common.base.Joiner;
024 import com.google.common.collect.ImmutableSet;
025 import org.apache.commons.lang.StringUtils;
026 import org.apache.commons.lang.builder.EqualsBuilder;
027 import org.apache.commons.lang.builder.HashCodeBuilder;
028 import org.apache.commons.lang.builder.ToStringBuilder;
029 import org.apache.commons.lang.builder.ToStringStyle;
030 import org.sonar.api.database.DatabaseProperties;
031 import org.sonar.api.rule.RuleKey;
032 import org.sonar.api.utils.SonarException;
033 import org.sonar.check.Cardinality;
034
035 import javax.annotation.CheckForNull;
036 import javax.annotation.Nullable;
037 import javax.persistence.*;
038 import java.util.ArrayList;
039 import java.util.Date;
040 import java.util.List;
041 import java.util.Set;
042
043 @Entity
044 @Table(name = "rules")
045 public final class Rule {
046
047 /**
048 * @since 3.6
049 */
050 public static final String STATUS_BETA = "BETA";
051 /**
052 * @since 3.6
053 */
054 public static final String STATUS_DEPRECATED = "DEPRECATED";
055 /**
056 * @since 3.6
057 */
058 public static final String STATUS_READY = "READY";
059
060 /**
061 * For internal use only.
062 *
063 * @since 3.6
064 */
065 public static final String STATUS_REMOVED = "REMOVED";
066
067 /**
068 * List of available status
069 *
070 * @since 3.6
071 */
072 private static final Set<String> STATUS_LIST = ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED);
073
074 @Id
075 @Column(name = "id")
076 @GeneratedValue
077 private Integer id;
078
079 /**
080 * The default priority given to a rule if not explicitly set
081 */
082 public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR;
083
084 @Column(name = "name", updatable = true, nullable = true, length = 200)
085 private String name;
086
087 @Column(name = "plugin_rule_key", updatable = false, nullable = true, length = 200)
088 private String key;
089
090 @Column(name = "plugin_config_key", updatable = true, nullable = true, length = 500)
091 private String configKey;
092
093 @Column(name = "priority", updatable = true, nullable = true)
094 @Enumerated(EnumType.ORDINAL)
095 private RulePriority priority = DEFAULT_PRIORITY;
096
097 @Column(name = "description", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE)
098 private String description;
099
100 @Column(name = "plugin_name", updatable = true, nullable = false)
101 private String pluginName;
102
103 @Enumerated(EnumType.STRING)
104 @Column(name = "cardinality", updatable = true, nullable = false)
105 private Cardinality cardinality = Cardinality.SINGLE;
106
107 @Column(name = "status", updatable = true, nullable = true)
108 private String status = STATUS_READY;
109
110 @Column(name = "language", updatable = true, nullable = true)
111 private String language;
112
113 @ManyToOne(fetch = FetchType.EAGER)
114 @JoinColumn(name = "parent_id", updatable = true, nullable = true)
115 private Rule parent = null;
116
117 @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
118 @OneToMany(mappedBy = "rule")
119 private List<RuleParam> params = new ArrayList<RuleParam>();
120
121 @Temporal(TemporalType.TIMESTAMP)
122 @Column(name = "created_at", updatable = true, nullable = true)
123 private Date createdAt;
124
125 @Temporal(TemporalType.TIMESTAMP)
126 @Column(name = "updated_at", updatable = true, nullable = true)
127 private Date updatedAt;
128
129 /**
130 * @deprecated since 2.3. Use the factory method {@link #create()}
131 */
132 @Deprecated
133 public Rule() {
134 // TODO reduce visibility to packaete
135 }
136
137 /**
138 * Creates rule with minimum set of info
139 *
140 * @param pluginName the plugin name indicates which plugin the rule belongs to
141 * @param key the key should be unique within a plugin, but it is even more careful for the time being that it is unique across the
142 * application
143 * @deprecated since 2.3. Use the factory method {@link #create()}
144 */
145 @Deprecated
146 public Rule(String pluginName, String key) {
147 this.pluginName = pluginName;
148 this.key = key;
149 this.configKey = key;
150 }
151
152 public Integer getId() {
153 return id;
154 }
155
156 /**
157 * @deprecated since 2.3. visibility should be decreased to protected or package
158 */
159 @Deprecated
160 public void setId(Integer id) {
161 this.id = id;
162 }
163
164 @CheckForNull
165 public String getName() {
166 return name;
167 }
168
169 /**
170 * Sets the rule name
171 */
172 public Rule setName(@Nullable String name) {
173 this.name = removeNewLineCharacters(name);
174 return this;
175 }
176
177 public String getKey() {
178 return key;
179 }
180
181 /**
182 * Sets the rule key
183 */
184 public Rule setKey(String key) {
185 this.key = key;
186 return this;
187 }
188
189 /**
190 * @deprecated since 2.5 use {@link #getRepositoryKey()} instead
191 */
192 @Deprecated
193 public String getPluginName() {
194 return pluginName;
195 }
196
197 /**
198 * @deprecated since 2.5 use {@link #setRepositoryKey(String)} instead
199 */
200 @Deprecated
201 public Rule setPluginName(String pluginName) {
202 this.pluginName = pluginName;
203 return this;
204 }
205
206 public String getConfigKey() {
207 return configKey;
208 }
209
210 /**
211 * Sets the configuration key
212 */
213 public Rule setConfigKey(String configKey) {
214 this.configKey = configKey;
215 return this;
216 }
217
218 public String getDescription() {
219 return description;
220 }
221
222 /**
223 * Sets the rule description
224 */
225 public Rule setDescription(String description) {
226 this.description = StringUtils.strip(description);
227 return this;
228 }
229
230 /**
231 * @deprecated in 3.6. Replaced by {@link #setStatus(String status)}.
232 */
233 @Deprecated
234 public Rule setEnabled(Boolean enabled) {
235 throw new UnsupportedOperationException("No more supported since version 3.6.");
236 }
237
238 public Boolean isEnabled() {
239 return !STATUS_REMOVED.equals(status);
240 }
241
242 public List<RuleParam> getParams() {
243 return params;
244 }
245
246 public RuleParam getParam(String key) {
247 for (RuleParam param : params) {
248 if (StringUtils.equals(key, param.getKey())) {
249 return param;
250 }
251 }
252 return null;
253 }
254
255 /**
256 * Sets the rule parameters
257 */
258 public Rule setParams(List<RuleParam> params) {
259 this.params.clear();
260 for (RuleParam param : params) {
261 param.setRule(this);
262 this.params.add(param);
263 }
264 return this;
265 }
266
267 public RuleParam createParameter() {
268 RuleParam parameter = new RuleParam()
269 .setRule(this);
270 params.add(parameter);
271 return parameter;
272 }
273
274 public RuleParam createParameter(String key) {
275 RuleParam parameter = new RuleParam()
276 .setKey(key)
277 .setRule(this);
278 params.add(parameter);
279 return parameter;
280 }
281
282 /**
283 * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2007
284 */
285 @Deprecated
286 public Integer getCategoryId() {
287 return null;
288 }
289
290 /**
291 * @since 2.5
292 */
293 public RulePriority getSeverity() {
294 return priority;
295 }
296
297 /**
298 * @param severity severity to set, if null, uses the default priority.
299 * @since 2.5
300 */
301 public Rule setSeverity(RulePriority severity) {
302 if (severity == null) {
303 this.priority = DEFAULT_PRIORITY;
304 } else {
305 this.priority = severity;
306 }
307 return this;
308 }
309
310 /**
311 * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829
312 */
313 @Deprecated
314 public RulePriority getPriority() {
315 return priority;
316 }
317
318 /**
319 * Sets the rule priority. If null, uses the default priority
320 *
321 * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829
322 */
323 @Deprecated
324 public Rule setPriority(RulePriority priority) {
325 return setSeverity(priority);
326 }
327
328 public String getRepositoryKey() {
329 return pluginName;
330 }
331
332 public Rule setRepositoryKey(String s) {
333 this.pluginName = s;
334 return this;
335 }
336
337 public Rule setUniqueKey(String repositoryKey, String key) {
338 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key);
339 }
340
341 public Cardinality getCardinality() {
342 return cardinality;
343 }
344
345 public Rule setCardinality(Cardinality c) {
346 this.cardinality = c;
347 return this;
348 }
349
350 public Rule getParent() {
351 return parent;
352 }
353
354 public Rule setParent(Rule parent) {
355 this.parent = parent;
356 return this;
357 }
358
359 /**
360 * @since 3.6
361 */
362 public String getStatus() {
363 return status;
364 }
365
366 /**
367 * @since 3.6
368 */
369 public Rule setStatus(String status) {
370 if (!STATUS_LIST.contains(status)) {
371 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST));
372 }
373 this.status = status;
374 return this;
375 }
376
377 /**
378 * @since 3.6
379 */
380 public Date getCreatedAt() {
381 return createdAt;
382 }
383
384 /**
385 * @since 3.6
386 */
387 public Rule setCreatedAt(Date d) {
388 this.createdAt = d;
389 return this;
390 }
391
392 /**
393 * @since 3.6
394 */
395 public Date getUpdatedAt() {
396 return updatedAt;
397 }
398
399 /**
400 * @since 3.6
401 */
402 public Rule setUpdatedAt(Date updatedAt) {
403 this.updatedAt = updatedAt;
404 return this;
405 }
406
407 /**
408 * @since 3.6
409 */
410 public String getLanguage() {
411 return language;
412 }
413
414 /**
415 * For internal use only.
416 *
417 * @since 3.6
418 */
419 public Rule setLanguage(String language) {
420 this.language = language;
421 return this;
422 }
423
424 @Override
425 public boolean equals(Object obj) {
426 if (!(obj instanceof Rule)) {
427 return false;
428 }
429 if (this == obj) {
430 return true;
431 }
432 Rule other = (Rule) obj;
433 return new EqualsBuilder()
434 .append(pluginName, other.getRepositoryKey())
435 .append(key, other.getKey())
436 .isEquals();
437 }
438
439 @Override
440 public int hashCode() {
441 return new HashCodeBuilder(17, 37)
442 .append(pluginName)
443 .append(key)
444 .toHashCode();
445 }
446
447 @Override
448 public String toString() {
449 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
450 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
451 .append("id", id)
452 .append("name", name)
453 .append("key", key)
454 .append("configKey", configKey)
455 .append("plugin", pluginName)
456 .append("severity", priority)
457 .append("cardinality", cardinality)
458 .append("status", status)
459 .append("language", language)
460 .append("parent", parent)
461 .toString();
462 }
463
464 @CheckForNull
465 private String removeNewLineCharacters(@Nullable String text) {
466 String removedCRLF = StringUtils.remove(text, "\n");
467 removedCRLF = StringUtils.remove(removedCRLF, "\r");
468 removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
469 removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
470 return removedCRLF;
471 }
472
473 public static Rule create() {
474 return new Rule();
475 }
476
477 /**
478 * Create with all required fields
479 */
480 public static Rule create(String repositoryKey, String key, String name) {
481 return new Rule().setUniqueKey(repositoryKey, key).setName(name);
482 }
483
484 /**
485 * Create with all required fields
486 *
487 * @since 2.10
488 */
489 public static Rule create(String repositoryKey, String key) {
490 return new Rule().setUniqueKey(repositoryKey, key);
491 }
492
493 /**
494 * @since 3.6
495 */
496 public RuleKey ruleKey() {
497 return RuleKey.of(getRepositoryKey(), getKey());
498 }
499 }