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 template;
291  }
292
293  /**
294   * @deprecated since 4.4, use {@link #setTemplate(Rule)}}
295   */
296  @Deprecated
297  public Rule setParent(Rule parent) {
298    this.template = parent;
299    return this;
300  }
301
302  /**
303   * @since 4.4
304   */
305  public Rule getTemplate() {
306    return template;
307  }
308
309  /**
310   * @since 4.4
311   */
312  public Rule setTemplate(Rule template) {
313    this.template = template;
314    return this;
315  }
316
317  /**
318   * @since 3.6
319   */
320  public String getStatus() {
321    return status;
322  }
323
324  /**
325   * @since 3.6
326   */
327  public Rule setStatus(String status) {
328    if (!STATUS_LIST.contains(status)) {
329      throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST));
330    }
331    this.status = status;
332    return this;
333  }
334
335  /**
336   * @since 3.6
337   */
338  public Date getCreatedAt() {
339    return createdAt;
340  }
341
342  /**
343   * @since 3.6
344   */
345  public Rule setCreatedAt(Date d) {
346    this.createdAt = d;
347    return this;
348  }
349
350  /**
351   * @since 3.6
352   */
353  public Date getUpdatedAt() {
354    return updatedAt;
355  }
356
357  /**
358   * @since 3.6
359   */
360  public Rule setUpdatedAt(Date updatedAt) {
361    this.updatedAt = updatedAt;
362    return this;
363  }
364
365  /**
366   * @since 3.6
367   */
368  public String getLanguage() {
369    return language;
370  }
371
372  /**
373   * For internal use only.
374   *
375   * @since 3.6
376   */
377  public Rule setLanguage(String language) {
378    this.language = language;
379    return this;
380  }
381
382  /**
383   * For definition of rule only
384   */
385  public String[] getTags() {
386    return tags == null ? new String[0] : StringUtils.split(tags, ',');
387  }
388
389  /**
390   * For definition of rule only
391   */
392  public Rule setTags(String[] tags) {
393    this.tags = tags == null ? null : StringUtils.join(tags, ',');
394    return this;
395  }
396
397  /**
398   * For internal use
399   */
400  public String[] getSystemTags() {
401    return systemTags == null ? new String[0] : StringUtils.split(systemTags, ',');
402  }
403
404  /**
405   * For internal use only.
406   *
407   * @deprecated since 4.4, use {@link #getCharacteristicKey()}
408   * @since 4.3
409   */
410  @CheckForNull
411  @Deprecated
412  public Integer getCharacteristicId() {
413    return null;
414  }
415
416  /**
417   * For internal use only.
418   *
419   * @deprecated since 4.4, use {@link #setCharacteristicKey(String)}
420   * @since 4.3
421   */
422  @Deprecated
423  public Rule setCharacteristicId(@Nullable Integer characteristicId) {
424    return this;
425  }
426
427  /**
428   * For internal use only.
429   *
430   * @deprecated since 4.4, use {@link #getDefaultCharacteristicKey()}
431   * @since 4.3
432   */
433  @CheckForNull
434  @Deprecated
435  public Integer getDefaultCharacteristicId() {
436    return null;
437  }
438
439  /**
440   * For internal use only.
441   *
442   * @deprecated since 4.4, use {@link #setDefaultCharacteristicKey(String)}
443   * @since 4.3
444   */
445  @Deprecated
446  public Rule setDefaultCharacteristicId(@Nullable Integer defaultCharacteristicId) {
447    return this;
448  }
449
450  @Override
451  public boolean equals(Object obj) {
452    if (!(obj instanceof Rule)) {
453      return false;
454    }
455    if (this == obj) {
456      return true;
457    }
458    Rule other = (Rule) obj;
459    return new EqualsBuilder()
460      .append(pluginName, other.getRepositoryKey())
461      .append(key, other.getKey())
462      .isEquals();
463  }
464
465  @Override
466  public int hashCode() {
467    return new HashCodeBuilder(17, 37)
468      .append(pluginName)
469      .append(key)
470      .toHashCode();
471  }
472
473  @Override
474  public String toString() {
475    // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
476    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
477      .append("id", id)
478      .append("name", name)
479      .append("key", key)
480      .append("configKey", configKey)
481      .append("plugin", pluginName)
482      .append("severity", priority)
483      .append("isTemplate", isTemplate())
484      .append("status", status)
485      .append("language", language)
486      .append("template", template)
487      .toString();
488  }
489
490  @CheckForNull
491  private static String removeNewLineCharacters(@Nullable String text) {
492    String removedCRLF = StringUtils.remove(text, "\n");
493    removedCRLF = StringUtils.remove(removedCRLF, "\r");
494    removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
495    removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
496    return removedCRLF;
497  }
498
499  public static Rule create() {
500    return new Rule();
501  }
502
503  /**
504   * Create with all required fields
505   */
506  public static Rule create(String repositoryKey, String key, String name) {
507    return new Rule().setUniqueKey(repositoryKey, key).setName(name);
508  }
509
510  /**
511   * Create with all required fields
512   *
513   * @since 2.10
514   */
515  public static Rule create(String repositoryKey, String key) {
516    return new Rule().setUniqueKey(repositoryKey, key);
517  }
518
519  /**
520   * @since 3.6
521   */
522  public RuleKey ruleKey() {
523    return RuleKey.of(getRepositoryKey(), getKey());
524  }
525
526  /**
527   * @since 4.4
528   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
529   */
530  @CheckForNull
531  @Deprecated
532  public String getDefaultCharacteristicKey() {
533    return null;
534  }
535
536  /**
537   * @since 4.4
538   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
539   */
540  @Deprecated
541  public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) {
542    return this;
543  }
544
545  /**
546   * @since 4.4
547   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
548   */
549  @CheckForNull
550  @Deprecated
551  public String getDefaultSubCharacteristicKey() {
552    return null;
553  }
554
555  /**
556   * @since 4.4
557   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
558   */
559  @Deprecated
560  public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) {
561    return this;
562  }
563
564  /**
565   * @since 4.4
566   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
567   */
568  @CheckForNull
569  @Deprecated
570  public String getCharacteristicKey() {
571    return null;
572  }
573
574  /**
575   * @since 4.4
576   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
577   */
578  @Deprecated
579  public Rule setCharacteristicKey(@Nullable String characteristicKey) {
580    return this;
581  }
582
583  /**
584   * @since 4.4
585   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
586   */
587  @CheckForNull
588  @Deprecated
589  public String getSubCharacteristicKey() {
590    return null;
591  }
592
593  /**
594   * @since 4.4
595   * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
596   */
597  @Deprecated
598  public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) {
599    return this;
600  }
601}