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
021package org.sonar.api.rules;
022
023import com.google.common.base.Joiner;
024import com.google.common.collect.ImmutableSet;
025import org.apache.commons.lang.StringUtils;
026import org.apache.commons.lang.builder.EqualsBuilder;
027import org.apache.commons.lang.builder.HashCodeBuilder;
028import org.apache.commons.lang.builder.ToStringBuilder;
029import org.apache.commons.lang.builder.ToStringStyle;
030import org.sonar.api.database.DatabaseProperties;
031import org.sonar.api.rule.RuleKey;
032import org.sonar.api.utils.SonarException;
033import org.sonar.check.Cardinality;
034
035import javax.annotation.CheckForNull;
036import javax.annotation.Nullable;
037import javax.persistence.*;
038import java.util.ArrayList;
039import java.util.Date;
040import java.util.List;
041import java.util.Set;
042
043@Entity
044@Table(name = "rules")
045public final class Rule {
046
047  /**
048   * @since 3.6
049   */
050  public static final String STATUS_BETA = "BETA";
051  /**
052   * @since 3.6
053   */
054  public static final String STATUS_DEPRECATED = "DEPRECATED";
055  /**
056   * @since 3.6
057   */
058  public static final String STATUS_READY = "READY";
059
060  /**
061   * For internal use only.
062   *
063   * @since 3.6
064   */
065  public static final String STATUS_REMOVED = "REMOVED";
066
067  /**
068   * List of available status
069   *
070   * @since 3.6
071   */
072  private static final Set<String> STATUS_LIST = ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED);
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  public Integer getId() {
153    return id;
154  }
155
156  /**
157   * @deprecated since 2.3. visibility should be decreased to protected or package
158   */
159  @Deprecated
160  public void setId(Integer id) {
161    this.id = id;
162  }
163
164  @CheckForNull
165  public String getName() {
166    return name;
167  }
168
169  /**
170   * Sets the rule name
171   */
172  public Rule setName(@Nullable String name) {
173    this.name = removeNewLineCharacters(name);
174    return this;
175  }
176
177  public String getKey() {
178    return key;
179  }
180
181  /**
182   * Sets the rule key
183   */
184  public Rule setKey(String key) {
185    this.key = key;
186    return this;
187  }
188
189  /**
190   * @deprecated since 2.5 use {@link #getRepositoryKey()} instead
191   */
192  @Deprecated
193  public String getPluginName() {
194    return pluginName;
195  }
196
197  /**
198   * @deprecated since 2.5 use {@link #setRepositoryKey(String)} instead
199   */
200  @Deprecated
201  public Rule setPluginName(String pluginName) {
202    this.pluginName = pluginName;
203    return this;
204  }
205
206  public String getConfigKey() {
207    return configKey;
208  }
209
210  /**
211   * Sets the configuration key
212   */
213  public Rule setConfigKey(String configKey) {
214    this.configKey = configKey;
215    return this;
216  }
217
218  public String getDescription() {
219    return description;
220  }
221
222  /**
223   * Sets the rule description
224   */
225  public Rule setDescription(String description) {
226    this.description = StringUtils.strip(description);
227    return this;
228  }
229
230  /**
231   * @deprecated in 3.6. Replaced by {@link #setStatus(String status)}.
232   */
233  @Deprecated
234  public Rule setEnabled(Boolean enabled) {
235    throw new UnsupportedOperationException("No more supported since version 3.6.");
236  }
237
238  public Boolean isEnabled() {
239    return !STATUS_REMOVED.equals(status);
240  }
241
242  public List<RuleParam> getParams() {
243    return params;
244  }
245
246  public RuleParam getParam(String key) {
247    for (RuleParam param : params) {
248      if (StringUtils.equals(key, param.getKey())) {
249        return param;
250      }
251    }
252    return null;
253  }
254
255  /**
256   * Sets the rule parameters
257   */
258  public Rule setParams(List<RuleParam> params) {
259    this.params.clear();
260    for (RuleParam param : params) {
261      param.setRule(this);
262      this.params.add(param);
263    }
264    return this;
265  }
266
267  public RuleParam createParameter() {
268    RuleParam parameter = new RuleParam()
269      .setRule(this);
270    params.add(parameter);
271    return parameter;
272  }
273
274  public RuleParam createParameter(String key) {
275    RuleParam parameter = new RuleParam()
276      .setKey(key)
277      .setRule(this);
278    params.add(parameter);
279    return parameter;
280  }
281
282  /**
283   * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2007
284   */
285  @Deprecated
286  public Integer getCategoryId() {
287    return null;
288  }
289
290  /**
291   * @since 2.5
292   */
293  public RulePriority getSeverity() {
294    return priority;
295  }
296
297  /**
298   * @param severity severity to set, if null, uses the default priority.
299   * @since 2.5
300   */
301  public Rule setSeverity(RulePriority severity) {
302    if (severity == null) {
303      this.priority = DEFAULT_PRIORITY;
304    } else {
305      this.priority = severity;
306    }
307    return this;
308  }
309
310  /**
311   * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829
312   */
313  @Deprecated
314  public RulePriority getPriority() {
315    return priority;
316  }
317
318  /**
319   * Sets the rule priority. If null, uses the default priority
320   *
321   * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829
322   */
323  @Deprecated
324  public Rule setPriority(RulePriority priority) {
325    return setSeverity(priority);
326  }
327
328  public String getRepositoryKey() {
329    return pluginName;
330  }
331
332  public Rule setRepositoryKey(String s) {
333    this.pluginName = s;
334    return this;
335  }
336
337  public Rule setUniqueKey(String repositoryKey, String key) {
338    return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key);
339  }
340
341  public Cardinality getCardinality() {
342    return cardinality;
343  }
344
345  public Rule setCardinality(Cardinality c) {
346    this.cardinality = c;
347    return this;
348  }
349
350  public Rule getParent() {
351    return parent;
352  }
353
354  public Rule setParent(Rule parent) {
355    this.parent = parent;
356    return this;
357  }
358
359  /**
360   * @since 3.6
361   */
362  public String getStatus() {
363    return status;
364  }
365
366  /**
367   * @since 3.6
368   */
369  public Rule setStatus(String status) {
370    if (!STATUS_LIST.contains(status)) {
371      throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST));
372    }
373    this.status = status;
374    return this;
375  }
376
377  /**
378   * @since 3.6
379   */
380  public Date getCreatedAt() {
381    return createdAt;
382  }
383
384  /**
385   * @since 3.6
386   */
387  public Rule setCreatedAt(Date d) {
388    this.createdAt = d;
389    return this;
390  }
391
392  /**
393   * @since 3.6
394   */
395  public Date getUpdatedAt() {
396    return updatedAt;
397  }
398
399  /**
400   * @since 3.6
401   */
402  public Rule setUpdatedAt(Date updatedAt) {
403    this.updatedAt = updatedAt;
404    return this;
405  }
406
407  /**
408   * @since 3.6
409   */
410  public String getLanguage() {
411    return language;
412  }
413
414  /**
415   * For internal use only.
416   *
417   * @since 3.6
418   */
419  public Rule setLanguage(String language) {
420    this.language = language;
421    return this;
422  }
423
424  @Override
425  public boolean equals(Object obj) {
426    if (!(obj instanceof Rule)) {
427      return false;
428    }
429    if (this == obj) {
430      return true;
431    }
432    Rule other = (Rule) obj;
433    return new EqualsBuilder()
434      .append(pluginName, other.getRepositoryKey())
435      .append(key, other.getKey())
436      .isEquals();
437  }
438
439  @Override
440  public int hashCode() {
441    return new HashCodeBuilder(17, 37)
442      .append(pluginName)
443      .append(key)
444      .toHashCode();
445  }
446
447  @Override
448  public String toString() {
449    // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
450    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
451      .append("id", id)
452      .append("name", name)
453      .append("key", key)
454      .append("configKey", configKey)
455      .append("plugin", pluginName)
456      .append("severity", priority)
457      .append("cardinality", cardinality)
458      .append("status", status)
459      .append("language", language)
460      .append("parent", parent)
461      .toString();
462  }
463
464  @CheckForNull
465  private String removeNewLineCharacters(@Nullable String text) {
466    String removedCRLF = StringUtils.remove(text, "\n");
467    removedCRLF = StringUtils.remove(removedCRLF, "\r");
468    removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
469    removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
470    return removedCRLF;
471  }
472
473  public static Rule create() {
474    return new Rule();
475  }
476
477  /**
478   * Create with all required fields
479   */
480  public static Rule create(String repositoryKey, String key, String name) {
481    return new Rule().setUniqueKey(repositoryKey, key).setName(name);
482  }
483
484  /**
485   * Create with all required fields
486   *
487   * @since 2.10
488   */
489  public static Rule create(String repositoryKey, String key) {
490    return new Rule().setUniqueKey(repositoryKey, key);
491  }
492
493  /**
494   * @since 3.6
495   */
496  public RuleKey ruleKey() {
497    return RuleKey.of(getRepositoryKey(), getKey());
498  }
499}