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