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.profiles;
021
022import java.util.ArrayList;
023import java.util.List;
024import java.util.stream.Collectors;
025import javax.annotation.CheckForNull;
026import javax.annotation.Nullable;
027import org.apache.commons.lang.StringUtils;
028import org.apache.commons.lang.builder.EqualsBuilder;
029import org.apache.commons.lang.builder.HashCodeBuilder;
030import org.sonar.api.rules.ActiveRule;
031import org.sonar.api.rules.Rule;
032import org.sonar.api.rules.RulePriority;
033import org.sonar.api.utils.MessageException;
034
035/**
036 * This class is badly named. It should be "QualityProfile". Indeed it does not relate only to rules but to metric thresholds too.
037 */
038public class RulesProfile implements Cloneable {
039
040  /**
041   * Name of the default profile "Sonar Way"
042   * @deprecated in 4.2. Use your own constant.
043   */
044  @Deprecated
045  public static final String SONAR_WAY_NAME = "Sonar way";
046
047  /**
048   * Name of the default java profile "Sonar way with Findbugs"
049   * @deprecated in 4.2. Use your own constant.
050   */
051  @Deprecated
052  public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
053
054  /**
055   * Name of the default java profile "Sun checks"
056   * @deprecated in 4.2. Use your own constant.
057   */
058  @Deprecated
059  public static final String SUN_CONVENTIONS_NAME = "Sun checks";
060
061  private String name;
062  private Boolean defaultProfile = Boolean.FALSE;
063  private String language;
064  private List<ActiveRule> activeRules = new ArrayList<>();
065
066  /**
067   * @deprecated use the factory method create()
068   */
069  @Deprecated
070  public RulesProfile() {
071  }
072
073  /**
074   * @deprecated since 2.3. Use the factory method create()
075   */
076  @Deprecated
077  public RulesProfile(String name, String language) {
078    this.name = name;
079    this.language = language;
080    this.activeRules = new ArrayList<>();
081  }
082
083  /**
084   * @deprecated since 2.3. Use the factory method create()
085   */
086  @Deprecated
087  public RulesProfile(String name, String language, boolean defaultProfile, /* kept for backward-compatibility */boolean provided) {
088    this(name, language);
089    this.defaultProfile = defaultProfile;
090  }
091
092  public Integer getId() {
093    return null;
094  }
095
096  /**
097   * @return the profile name, unique by language.
098   */
099  public String getName() {
100    return name;
101  }
102
103  /**
104   * Set the profile name.
105   */
106  public RulesProfile setName(String s) {
107    this.name = s;
108    return this;
109  }
110
111  /**
112   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
113   */
114  @Deprecated
115  public int getVersion() {
116    return -1;
117  }
118
119  /**
120   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
121   */
122  @Deprecated
123  public RulesProfile setVersion(int version) {
124    // ignore
125    return this;
126  }
127
128  /**
129   * @deprecated profile versioning is dropped in 4.4. Always returns null.
130   */
131  @CheckForNull
132  @Deprecated
133  public Boolean getUsed() {
134    return null;
135  }
136
137  /**
138   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
139   */
140  @Deprecated
141  public RulesProfile setUsed(Boolean used) {
142    return this;
143  }
144
145  /**
146   * @return the list of active rules
147   */
148  public List<ActiveRule> getActiveRules() {
149    return getActiveRules(false);
150  }
151
152  /**
153   * @return the list of active rules
154   */
155  public List<ActiveRule> getActiveRules(boolean acceptDisabledRules) {
156    if (acceptDisabledRules) {
157      return activeRules;
158    }
159    List<ActiveRule> result = new ArrayList<>();
160    for (ActiveRule activeRule : activeRules) {
161      if (activeRule.isEnabled()) {
162        result.add(activeRule);
163      }
164    }
165    return result;
166  }
167
168  public RulesProfile removeActiveRule(ActiveRule activeRule) {
169    activeRules.remove(activeRule);
170    return this;
171  }
172
173  public RulesProfile addActiveRule(ActiveRule activeRule) {
174    activeRules.add(activeRule);
175    return this;
176  }
177
178  /**
179   * Set the list of active rules
180   */
181  public void setActiveRules(List<ActiveRule> activeRules) {
182    this.activeRules = activeRules;
183  }
184
185  /**
186   * @return whether this is the default profile for the language
187   */
188  public Boolean getDefaultProfile() {
189    return defaultProfile;
190  }
191
192  /**
193   * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when auditing a
194   * project.
195   */
196  public void setDefaultProfile(Boolean b) {
197    this.defaultProfile = b;
198  }
199
200  /**
201   * @return the profile language
202   */
203  public String getLanguage() {
204    return language;
205  }
206
207  /**
208   * Set the profile language
209   */
210  public RulesProfile setLanguage(String s) {
211    this.language = s;
212    return this;
213  }
214
215  /**
216   * Does nothing.
217   *
218   * @return {@code null}
219   * @deprecated in 6.5
220   */
221  @Deprecated
222  @CheckForNull
223  public String getParentName() {
224    return null;
225  }
226
227  /**
228   * Does nothing.
229   *
230   * @deprecated in 6.5
231   */
232  @Deprecated
233  public void setParentName(String parentName) {
234    // does nothing
235  }
236
237  /**
238   * Note: disabled rules are excluded.
239   *
240   * @return the list of active rules for a given severity
241   */
242  public List<ActiveRule> getActiveRules(RulePriority severity) {
243    List<ActiveRule> result = new ArrayList<>();
244    for (ActiveRule activeRule : activeRules) {
245      if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
246        result.add(activeRule);
247      }
248    }
249    return result;
250  }
251
252  /**
253   * Get the active rules of a specific repository.
254   * Only enabled rules are selected. Disabled rules are excluded.
255   */
256  public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
257    List<ActiveRule> result = new ArrayList<>();
258    for (ActiveRule activeRule : activeRules) {
259      if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
260        result.add(activeRule);
261      }
262    }
263    return result;
264  }
265
266  /**
267   * Note: disabled rules are excluded.
268   *
269   * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
270   */
271  @CheckForNull
272  public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
273    for (ActiveRule activeRule : activeRules) {
274      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
275        return activeRule;
276      }
277    }
278    return null;
279  }
280
281  /**
282   * Note: disabled rules are excluded.
283   */
284  @CheckForNull
285  public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
286    for (ActiveRule activeRule : activeRules) {
287      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
288        return activeRule;
289      }
290    }
291    return null;
292  }
293
294  /**
295   * Note: disabled rules are excluded.
296   */
297  @CheckForNull
298  public ActiveRule getActiveRule(Rule rule) {
299    return getActiveRule(rule.getRepositoryKey(), rule.getKey());
300  }
301
302  /**
303   * @param optionalSeverity if null, then the default rule severity is used
304   */
305  public ActiveRule activateRule(final Rule rule, @Nullable RulePriority optionalSeverity) {
306    if (activeRules.stream().anyMatch(ar -> ar.getRule().equals(rule))) {
307      throw MessageException.of(String.format(
308        "The definition of the profile '%s' (language '%s') contains multiple occurrences of the '%s:%s' rule. The plugin which declares this profile should fix this.",
309        getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey()));
310    }
311    ActiveRule activeRule = new ActiveRule(this, rule, optionalSeverity);
312    activeRules.add(activeRule);
313    return activeRule;
314  }
315
316  @Override
317  public boolean equals(Object obj) {
318    if (!(obj instanceof RulesProfile)) {
319      return false;
320    }
321    if (this == obj) {
322      return true;
323    }
324    RulesProfile other = (RulesProfile) obj;
325    return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
326  }
327
328  @Override
329  public int hashCode() {
330    return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
331  }
332
333  @Override
334  public Object clone() {
335    RulesProfile clone = RulesProfile.create(getName(), getLanguage());
336    clone.setDefaultProfile(getDefaultProfile());
337    if (activeRules != null && !activeRules.isEmpty()) {
338      clone.setActiveRules(activeRules.stream()
339        .map(ar -> (ActiveRule) ar.clone())
340        .collect(Collectors.toList()));
341    }
342    return clone;
343  }
344
345  @Override
346  public String toString() {
347    return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
348  }
349
350  public static RulesProfile create(String name, String language) {
351    return new RulesProfile().setName(name).setLanguage(language);
352  }
353
354  public static RulesProfile create() {
355    return new RulesProfile();
356  }
357
358}