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