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