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