001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 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.Column;
038 import javax.persistence.Entity;
039 import javax.persistence.EnumType;
040 import javax.persistence.Enumerated;
041 import javax.persistence.FetchType;
042 import javax.persistence.GeneratedValue;
043 import javax.persistence.Id;
044 import javax.persistence.JoinColumn;
045 import javax.persistence.ManyToOne;
046 import javax.persistence.OneToMany;
047 import javax.persistence.Table;
048 import javax.persistence.Temporal;
049 import javax.persistence.TemporalType;
050 import javax.persistence.Transient;
051
052 import java.util.ArrayList;
053 import java.util.Date;
054 import java.util.List;
055 import java.util.Set;
056
057 @Entity
058 @Table(name = "rules")
059 public class Rule {
060
061 /**
062 * @since 3.6
063 */
064 public static final String STATUS_BETA = "BETA";
065 /**
066 * @since 3.6
067 */
068 public static final String STATUS_DEPRECATED = "DEPRECATED";
069 /**
070 * @since 3.6
071 */
072 public static final String STATUS_READY = "READY";
073
074 /**
075 * For internal use only.
076 *
077 * @since 3.6
078 */
079 public static final String STATUS_REMOVED = "REMOVED";
080
081 /**
082 * List of available status
083 *
084 * @since 3.6
085 */
086 private static final Set<String> STATUS_LIST = ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED);
087
088 /**
089 * @since 4.2
090 */
091 private static final String[] DEFAULT_TAGS = new String[0];
092
093 @Id
094 @Column(name = "id")
095 @GeneratedValue
096 private Integer id;
097
098 /**
099 * The default priority given to a rule if not explicitly set
100 */
101 public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR;
102
103 @Column(name = "name", updatable = true, nullable = true, length = 200)
104 private String name;
105
106 @Column(name = "plugin_rule_key", updatable = false, nullable = true, length = 200)
107 private String key;
108
109 @Column(name = "plugin_config_key", updatable = true, nullable = true, length = 500)
110 private String configKey;
111
112 @Column(name = "priority", updatable = true, nullable = true)
113 @Enumerated(EnumType.ORDINAL)
114 private RulePriority priority = DEFAULT_PRIORITY;
115
116 @Column(name = "description", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE)
117 private String description;
118
119 @Column(name = "plugin_name", updatable = true, nullable = false)
120 private String pluginName;
121
122 @Enumerated(EnumType.STRING)
123 @Column(name = "is_template", updatable = true, nullable = false)
124 private boolean isTemplate = false;
125
126 @Column(name = "status", updatable = true, nullable = true)
127 private String status = STATUS_READY;
128
129 @Column(name = "language", updatable = true, nullable = true)
130 private String language;
131
132 @ManyToOne(fetch = FetchType.EAGER)
133 @JoinColumn(name = "template_id", updatable = true, nullable = true)
134 private Rule template = null;
135
136 @Column(name = "characteristic_id", updatable = true, nullable = true)
137 private Integer characteristicId;
138
139 @Column(name = "default_characteristic_id", updatable = true, nullable = true)
140 private Integer defaultCharacteristicId;
141
142 @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
143 @OneToMany(mappedBy = "rule")
144 private List<RuleParam> params = new ArrayList<RuleParam>();
145
146 @Temporal(TemporalType.TIMESTAMP)
147 @Column(name = "created_at", updatable = true, nullable = true)
148 private Date createdAt;
149
150 @Temporal(TemporalType.TIMESTAMP)
151 @Column(name = "updated_at", updatable = true, nullable = true)
152 private Date updatedAt;
153
154 @Transient
155 private String defaultCharacteristicKey;
156 @Transient
157 private String defaultSubCharacteristicKey;
158 @Transient
159 private String characteristicKey;
160 @Transient
161 private String subCharacteristicKey;
162
163 private transient String[] tags = DEFAULT_TAGS;
164
165 /**
166 * @deprecated since 2.3. Use the factory method {@link #create()}
167 */
168 @Deprecated
169 public Rule() {
170 }
171
172 /**
173 * Creates rule with minimum set of info
174 *
175 * @param pluginName the plugin name indicates which plugin the rule belongs to
176 * @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
177 * application
178 * @deprecated since 2.3. Use the factory method {@link #create()}
179 */
180 @Deprecated
181 public Rule(String pluginName, String key) {
182 this.pluginName = pluginName;
183 this.key = key;
184 this.configKey = key;
185 }
186
187 public Integer getId() {
188 return id;
189 }
190
191 /**
192 * @deprecated since 2.3. visibility should be decreased to protected or package
193 */
194 @Deprecated
195 public void setId(Integer id) {
196 this.id = id;
197 }
198
199 public String getName() {
200 return name;
201 }
202
203 /**
204 * Sets the rule name
205 */
206 public Rule setName(String name) {
207 this.name = removeNewLineCharacters(name);
208 return this;
209 }
210
211 public String getKey() {
212 return key;
213 }
214
215 /**
216 * Sets the rule key
217 */
218 public Rule setKey(String key) {
219 this.key = key;
220 return this;
221 }
222
223 public String getConfigKey() {
224 return configKey;
225 }
226
227 /**
228 * Sets the configuration key
229 */
230 public Rule setConfigKey(String configKey) {
231 this.configKey = configKey;
232 return this;
233 }
234
235 public String getDescription() {
236 return description;
237 }
238
239 /**
240 * Sets the rule description
241 */
242 public Rule setDescription(String description) {
243 this.description = StringUtils.strip(description);
244 return this;
245 }
246
247 public Boolean isEnabled() {
248 return !STATUS_REMOVED.equals(status);
249 }
250
251 public List<RuleParam> getParams() {
252 return params;
253 }
254
255 public RuleParam getParam(String key) {
256 for (RuleParam param : params) {
257 if (StringUtils.equals(key, param.getKey())) {
258 return param;
259 }
260 }
261 return null;
262 }
263
264 /**
265 * Sets the rule parameters
266 */
267 public Rule setParams(List<RuleParam> params) {
268 this.params.clear();
269 for (RuleParam param : params) {
270 param.setRule(this);
271 this.params.add(param);
272 }
273 return this;
274 }
275
276 public RuleParam createParameter() {
277 RuleParam parameter = new RuleParam()
278 .setRule(this);
279 params.add(parameter);
280 return parameter;
281 }
282
283 public RuleParam createParameter(String key) {
284 RuleParam parameter = new RuleParam()
285 .setKey(key)
286 .setRule(this);
287 params.add(parameter);
288 return parameter;
289 }
290
291 /**
292 * @since 2.5
293 */
294 public RulePriority getSeverity() {
295 return priority;
296 }
297
298 /**
299 * @param severity severity to set, if null, uses the default priority.
300 * @since 2.5
301 */
302 public Rule setSeverity(RulePriority severity) {
303 if (severity == null) {
304 this.priority = DEFAULT_PRIORITY;
305 } else {
306 this.priority = severity;
307 }
308 return this;
309 }
310
311 public String getRepositoryKey() {
312 return pluginName;
313 }
314
315 public Rule setRepositoryKey(String s) {
316 this.pluginName = s;
317 return this;
318 }
319
320 public Rule setUniqueKey(String repositoryKey, String key) {
321 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key);
322 }
323
324 /**
325 * @since 4.4
326 */
327 public boolean isTemplate() {
328 return isTemplate;
329 }
330
331 /**
332 * @since 4.4
333 */
334 public Rule setIsTemplate(boolean isTemplate) {
335 this.isTemplate = isTemplate;
336 return this;
337 }
338
339 /**
340 * @deprecated since 4.4, use {@link #isTemplate()}
341 */
342 @Deprecated
343 public Cardinality getCardinality() {
344 return isTemplate ? Cardinality.MULTIPLE : Cardinality.SINGLE;
345 }
346
347 /**
348 * @deprecated since 4.4, use {@link #setIsTemplate(boolean)}
349 */
350 @Deprecated
351 public Rule setCardinality(Cardinality c) {
352 this.isTemplate = Cardinality.MULTIPLE.equals(c);
353 return this;
354 }
355
356 /**
357 * @deprecated since 4.4, use {@link #getTemplate()}
358 */
359 @Deprecated
360 public Rule getParent() {
361 return template;
362 }
363
364 /**
365 * @deprecated since 4.4, use {@link #setTemplate(Rule)}}
366 */
367 @Deprecated
368 public Rule setParent(Rule parent) {
369 this.template = parent;
370 return this;
371 }
372
373 /**
374 * @since 4.4
375 */
376 public Rule getTemplate() {
377 return template;
378 }
379
380 /**
381 * @since 4.4
382 */
383 public Rule setTemplate(Rule template) {
384 this.template = template;
385 return this;
386 }
387
388 /**
389 * @since 3.6
390 */
391 public String getStatus() {
392 return status;
393 }
394
395 /**
396 * @since 3.6
397 */
398 public Rule setStatus(String status) {
399 if (!STATUS_LIST.contains(status)) {
400 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST));
401 }
402 this.status = status;
403 return this;
404 }
405
406 /**
407 * @since 3.6
408 */
409 public Date getCreatedAt() {
410 return createdAt;
411 }
412
413 /**
414 * @since 3.6
415 */
416 public Rule setCreatedAt(Date d) {
417 this.createdAt = d;
418 return this;
419 }
420
421 /**
422 * @since 3.6
423 */
424 public Date getUpdatedAt() {
425 return updatedAt;
426 }
427
428 /**
429 * @since 3.6
430 */
431 public Rule setUpdatedAt(Date updatedAt) {
432 this.updatedAt = updatedAt;
433 return this;
434 }
435
436 /**
437 * @since 3.6
438 */
439 public String getLanguage() {
440 return language;
441 }
442
443 /**
444 * For internal use only.
445 *
446 * @since 3.6
447 */
448 public Rule setLanguage(String language) {
449 this.language = language;
450 return this;
451 }
452
453 /**
454 * For definition of rule only
455 */
456 public String[] getTags() {
457 return tags;
458 }
459
460 /**
461 * For definition of rule only
462 */
463 public Rule setTags(String[] tags) {
464 this.tags = tags;
465 return this;
466 }
467
468 /**
469 * For internal use only.
470 *
471 * @deprecated since 4.4, use {@link #getCharacteristicKey()}
472 * @since 4.3
473 */
474 @CheckForNull
475 @Deprecated
476 public Integer getCharacteristicId() {
477 return characteristicId;
478 }
479
480 /**
481 * For internal use only.
482 *
483 * @deprecated since 4.4, use {@link #setCharacteristicKey(String)}
484 * @since 4.3
485 */
486 @Deprecated
487 public Rule setCharacteristicId(@Nullable Integer characteristicId) {
488 this.characteristicId = characteristicId;
489 return this;
490 }
491
492 /**
493 * For internal use only.
494 *
495 * @deprecated since 4.4, use {@link #getDefaultCharacteristicKey()}
496 * @since 4.3
497 */
498 @CheckForNull
499 @Deprecated
500 public Integer getDefaultCharacteristicId() {
501 return defaultCharacteristicId;
502 }
503
504 /**
505 * For internal use only.
506 *
507 * @deprecated since 4.4, use {@link #setDefaultCharacteristicKey(String)}
508 * @since 4.3
509 */
510 @Deprecated
511 public Rule setDefaultCharacteristicId(@Nullable Integer defaultCharacteristicId) {
512 this.defaultCharacteristicId = defaultCharacteristicId;
513 return this;
514 }
515
516 @Override
517 public boolean equals(Object obj) {
518 if (!(obj instanceof Rule)) {
519 return false;
520 }
521 if (this == obj) {
522 return true;
523 }
524 Rule other = (Rule) obj;
525 return new EqualsBuilder()
526 .append(pluginName, other.getRepositoryKey())
527 .append(key, other.getKey())
528 .isEquals();
529 }
530
531 @Override
532 public int hashCode() {
533 return new HashCodeBuilder(17, 37)
534 .append(pluginName)
535 .append(key)
536 .toHashCode();
537 }
538
539 @Override
540 public String toString() {
541 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
542 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
543 .append("id", id)
544 .append("name", name)
545 .append("key", key)
546 .append("configKey", configKey)
547 .append("plugin", pluginName)
548 .append("severity", priority)
549 .append("isTemplate", isTemplate())
550 .append("status", status)
551 .append("language", language)
552 .append("template", template)
553 .toString();
554 }
555
556 @CheckForNull
557 private String removeNewLineCharacters(@Nullable String text) {
558 String removedCRLF = StringUtils.remove(text, "\n");
559 removedCRLF = StringUtils.remove(removedCRLF, "\r");
560 removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
561 removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
562 return removedCRLF;
563 }
564
565 public static Rule create() {
566 return new Rule();
567 }
568
569 /**
570 * Create with all required fields
571 */
572 public static Rule create(String repositoryKey, String key, String name) {
573 return new Rule().setUniqueKey(repositoryKey, key).setName(name);
574 }
575
576 /**
577 * Create with all required fields
578 *
579 * @since 2.10
580 */
581 public static Rule create(String repositoryKey, String key) {
582 return new Rule().setUniqueKey(repositoryKey, key);
583 }
584
585 /**
586 * @since 3.6
587 */
588 public RuleKey ruleKey() {
589 return RuleKey.of(getRepositoryKey(), getKey());
590 }
591
592 /**
593 * @since 4.4
594 */
595 @CheckForNull
596 public String getDefaultCharacteristicKey() {
597 return defaultCharacteristicKey;
598 }
599
600 /**
601 * @since 4.4
602 */
603 public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) {
604 this.defaultCharacteristicKey = defaultCharacteristicKey;
605 return this;
606 }
607
608 /**
609 * @since 4.4
610 */
611 @CheckForNull
612 public String getDefaultSubCharacteristicKey() {
613 return defaultSubCharacteristicKey;
614 }
615
616 /**
617 * @since 4.4
618 */
619 public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) {
620 this.defaultSubCharacteristicKey = defaultSubCharacteristicKey;
621 return this;
622 }
623
624 /**
625 * @since 4.4
626 */
627 @CheckForNull
628 public String getCharacteristicKey() {
629 return characteristicKey;
630 }
631
632 /**
633 * @since 4.4
634 */
635 public Rule setCharacteristicKey(@Nullable String characteristicKey) {
636 this.characteristicKey = characteristicKey;
637 return this;
638 }
639
640 /**
641 * @since 4.4
642 */
643 @CheckForNull
644 public String getSubCharacteristicKey() {
645 return subCharacteristicKey;
646 }
647
648 /**
649 * @since 4.4
650 */
651 public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) {
652 this.subCharacteristicKey = subCharacteristicKey;
653 return this;
654 }
655 }