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