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