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 /**
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 }