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