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  public Rule setSystemTags(String[] tags) {
404    this.systemTags = tags == null ? null : StringUtils.join(tags, ',');
405    return this;
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 null;
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    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 null;
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    return this;
452  }
453
454  @Override
455  public boolean equals(Object obj) {
456    if (!(obj instanceof Rule)) {
457      return false;
458    }
459    if (this == obj) {
460      return true;
461    }
462    Rule other = (Rule) obj;
463    return new EqualsBuilder()
464      .append(pluginName, other.getRepositoryKey())
465      .append(key, other.getKey())
466      .isEquals();
467  }
468
469  @Override
470  public int hashCode() {
471    return new HashCodeBuilder(17, 37)
472      .append(pluginName)
473      .append(key)
474      .toHashCode();
475  }
476
477  @Override
478  public String toString() {
479    // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
480    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
481      .append("id", id)
482      .append("name", name)
483      .append("key", key)
484      .append("configKey", configKey)
485      .append("plugin", pluginName)
486      .append("severity", priority)
487      .append("isTemplate", isTemplate())
488      .append("status", status)
489      .append("language", language)
490      .append("template", template)
491      .toString();
492  }
493
494  @CheckForNull
495  private static String removeNewLineCharacters(@Nullable String text) {
496    String removedCRLF = StringUtils.remove(text, "\n");
497    removedCRLF = StringUtils.remove(removedCRLF, "\r");
498    removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
499    removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
500    return removedCRLF;
501  }
502
503  public static Rule create() {
504    return new Rule();
505  }
506
507  /**
508   * Create with all required fields
509   */
510  public static Rule create(String repositoryKey, String key, String name) {
511    return new Rule().setUniqueKey(repositoryKey, key).setName(name);
512  }
513
514  /**
515   * Create with all required fields
516   *
517   * @since 2.10
518   */
519  public static Rule create(String repositoryKey, String key) {
520    return new Rule().setUniqueKey(repositoryKey, key);
521  }
522
523  /**
524   * @since 3.6
525   */
526  public RuleKey ruleKey() {
527    return RuleKey.of(getRepositoryKey(), getKey());
528  }
529
530  /**
531   * @since 4.4
532   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
533   */
534  @CheckForNull
535  @Deprecated
536  public String getDefaultCharacteristicKey() {
537    return null;
538  }
539
540  /**
541   * @since 4.4
542   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
543   */
544  @Deprecated
545  public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) {
546    return this;
547  }
548
549  /**
550   * @since 4.4
551   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
552   */
553  @CheckForNull
554  @Deprecated
555  public String getDefaultSubCharacteristicKey() {
556    return null;
557  }
558
559  /**
560   * @since 4.4
561   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
562   */
563  @Deprecated
564  public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) {
565    return this;
566  }
567
568  /**
569   * @since 4.4
570   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
571   */
572  @CheckForNull
573  @Deprecated
574  public String getCharacteristicKey() {
575    return null;
576  }
577
578  /**
579   * @since 4.4
580   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
581   */
582  @Deprecated
583  public Rule setCharacteristicKey(@Nullable String characteristicKey) {
584    return this;
585  }
586
587  /**
588   * @since 4.4
589   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
590   */
591  @CheckForNull
592  @Deprecated
593  public String getSubCharacteristicKey() {
594    return null;
595  }
596
597  /**
598   * @since 4.4
599   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
600   */
601  @Deprecated
602  public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) {
603    return this;
604  }
605}