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