001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 com.google.common.base.Predicate;
023import com.google.common.collect.Iterables;
024import com.google.common.collect.Lists;
025import org.apache.commons.collections.CollectionUtils;
026import org.apache.commons.collections.Transformer;
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
035import javax.annotation.CheckForNull;
036import javax.annotation.Nullable;
037
038import java.util.ArrayList;
039import java.util.List;
040
041/**
042 * This class is badly named. It should be "QualityProfile". Indeed it does not relate only to rules but to metric thresholds too.
043 */
044public class RulesProfile implements Cloneable {
045
046  /**
047   * Name of the default profile "Sonar Way"
048   * @deprecated in 4.2. Use your own constant.
049   */
050  @Deprecated
051  public static final String SONAR_WAY_NAME = "Sonar way";
052
053  /**
054   * Name of the default java profile "Sonar way with Findbugs"
055   * @deprecated in 4.2. Use your own constant.
056   */
057  @Deprecated
058  public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
059
060  /**
061   * Name of the default java profile "Sun checks"
062   * @deprecated in 4.2. Use your own constant.
063   */
064  @Deprecated
065  public static final String SUN_CONVENTIONS_NAME = "Sun checks";
066
067  private String name;
068  private Boolean defaultProfile = Boolean.FALSE;
069  private String language;
070  private String parentName;
071  private List<ActiveRule> activeRules = Lists.newArrayList();
072
073  /**
074   * @deprecated use the factory method create()
075   */
076  @Deprecated
077  public RulesProfile() {
078  }
079
080  /**
081   * @deprecated since 2.3. Use the factory method create()
082   */
083  @Deprecated
084  public RulesProfile(String name, String language) {
085    this.name = name;
086    this.language = language;
087    this.activeRules = Lists.newArrayList();
088  }
089
090  /**
091   * @deprecated since 2.3. Use the factory method create()
092   */
093  @Deprecated
094  public RulesProfile(String name, String language, boolean defaultProfile, /* kept for backward-compatibility */boolean provided) {
095    this(name, language);
096    this.defaultProfile = defaultProfile;
097  }
098
099  public Integer getId() {
100    return null;
101  }
102
103  /**
104   * @return the profile name, unique by language.
105   */
106  public String getName() {
107    return name;
108  }
109
110  /**
111   * Set the profile name.
112   */
113  public RulesProfile setName(String s) {
114    this.name = s;
115    return this;
116  }
117
118  /**
119   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
120   */
121  @Deprecated
122  public int getVersion() {
123    return -1;
124  }
125
126  /**
127   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
128   */
129  @Deprecated
130  public RulesProfile setVersion(int version) {
131    // ignore
132    return this;
133  }
134
135  /**
136   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
137   */
138  @CheckForNull
139  @Deprecated
140  public Boolean getUsed() {
141    return null;
142  }
143
144  /**
145   * @deprecated profile versioning is dropped in 4.4. Always returns -1.
146   */
147  @Deprecated
148  public RulesProfile setUsed(Boolean used) {
149    return this;
150  }
151
152  /**
153   * @return the list of active rules
154   */
155  public List<ActiveRule> getActiveRules() {
156    return getActiveRules(false);
157  }
158
159  /**
160   * @return the list of active rules
161   */
162  public List<ActiveRule> getActiveRules(boolean acceptDisabledRules) {
163    if (acceptDisabledRules) {
164      return activeRules;
165    }
166    List<ActiveRule> result = Lists.newArrayList();
167    for (ActiveRule activeRule : activeRules) {
168      if (activeRule.isEnabled()) {
169        result.add(activeRule);
170      }
171    }
172    return result;
173  }
174
175  public RulesProfile removeActiveRule(ActiveRule activeRule) {
176    activeRules.remove(activeRule);
177    return this;
178  }
179
180  public RulesProfile addActiveRule(ActiveRule activeRule) {
181    activeRules.add(activeRule);
182    return this;
183  }
184
185  /**
186   * Set the list of active rules
187   */
188  public void setActiveRules(List<ActiveRule> activeRules) {
189    this.activeRules = activeRules;
190  }
191
192  /**
193   * @return whether this is the default profile for the language
194   */
195  public Boolean getDefaultProfile() {
196    return defaultProfile;
197  }
198
199  /**
200   * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when auditing a
201   * project.
202   */
203  public void setDefaultProfile(Boolean b) {
204    this.defaultProfile = b;
205  }
206
207  /**
208   * @deprecated since 3.3 not replaced
209   */
210  @Deprecated
211  public Boolean getProvided() {
212    return false;
213  }
214
215  /**
216   * @deprecated since 3.3 not replaced
217   */
218  @Deprecated
219  public void setProvided(Boolean b) {
220  }
221
222  /**
223   * @deprecated since 3.3. Always return true.
224   */
225  @Deprecated
226  public Boolean getEnabled() {
227    return Boolean.TRUE;
228  }
229
230  /**
231   * @deprecated since 3.3. Always return true.
232   */
233  @Deprecated
234  public boolean isEnabled() {
235    return true;
236  }
237
238  /**
239   * @deprecated since 3.3.
240   */
241  @Deprecated
242  public RulesProfile setEnabled(Boolean b) {
243    throw new UnsupportedOperationException("The field RulesProfile#enabled is not supported since 3.3.");
244  }
245
246  /**
247   * @return the profile language
248   */
249  public String getLanguage() {
250    return language;
251  }
252
253  /**
254   * Set the profile language
255   */
256  public RulesProfile setLanguage(String s) {
257    this.language = s;
258    return this;
259  }
260
261  /**
262   * For internal use only.
263   *
264   * @since 2.5
265   */
266  @CheckForNull
267  public String getParentName() {
268    return parentName;
269  }
270
271  /**
272   * For internal use only.
273   *
274   * @since 2.5
275   */
276  public void setParentName(String parentName) {
277    this.parentName = parentName;
278  }
279
280  /**
281   * Note: disabled rules are excluded.
282   *
283   * @return the list of active rules for a given severity
284   */
285  public List<ActiveRule> getActiveRules(RulePriority severity) {
286    List<ActiveRule> result = Lists.newArrayList();
287    for (ActiveRule activeRule : activeRules) {
288      if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
289        result.add(activeRule);
290      }
291    }
292    return result;
293  }
294
295  /**
296   * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
297   */
298  @Deprecated
299  public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
300    return getActiveRulesByRepository(repositoryKey);
301  }
302
303  /**
304   * Get the active rules of a specific repository.
305   * Only enabled rules are selected. Disabled rules are excluded.
306   */
307  public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
308    List<ActiveRule> result = Lists.newArrayList();
309    for (ActiveRule activeRule : activeRules) {
310      if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
311        result.add(activeRule);
312      }
313    }
314    return result;
315  }
316
317  /**
318   * Note: disabled rules are excluded.
319   *
320   * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
321   */
322  @CheckForNull
323  public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
324    for (ActiveRule activeRule : activeRules) {
325      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
326        return activeRule;
327      }
328    }
329    return null;
330  }
331
332  /**
333   * Note: disabled rules are excluded.
334   */
335  @CheckForNull
336  public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
337    for (ActiveRule activeRule : activeRules) {
338      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
339        return activeRule;
340      }
341    }
342    return null;
343  }
344
345  /**
346   * Note: disabled rules are excluded.
347   */
348  @CheckForNull
349  public ActiveRule getActiveRule(Rule rule) {
350    return getActiveRule(rule.getRepositoryKey(), rule.getKey());
351  }
352
353  /**
354   * @param optionalSeverity if null, then the default rule severity is used
355   */
356  public ActiveRule activateRule(final Rule rule, @Nullable RulePriority optionalSeverity) {
357    if (Iterables.any(activeRules, new Predicate<ActiveRule>() {
358      @Override
359      public boolean apply(ActiveRule input) {
360        return input.getRule().equals(rule);
361      }
362    })) {
363      throw MessageException.of(String.format(
364        "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.",
365        getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey()));
366    }
367    ActiveRule activeRule = new ActiveRule();
368    activeRule.setRule(rule);
369    activeRule.setRulesProfile(this);
370    activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
371    activeRules.add(activeRule);
372    return activeRule;
373  }
374
375  @Override
376  public boolean equals(Object obj) {
377    if (!(obj instanceof RulesProfile)) {
378      return false;
379    }
380    if (this == obj) {
381      return true;
382    }
383    RulesProfile other = (RulesProfile) obj;
384    return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
385  }
386
387  @Override
388  public int hashCode() {
389    return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
390  }
391
392  @Override
393  public Object clone() {
394    RulesProfile clone = RulesProfile.create(getName(), getLanguage());
395    clone.setDefaultProfile(getDefaultProfile());
396    clone.setParentName(getParentName());
397    if (activeRules != null && !activeRules.isEmpty()) {
398      clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
399        public Object transform(Object input) {
400          return ((ActiveRule) input).clone();
401        }
402      })));
403    }
404    return clone;
405  }
406
407  @Override
408  public String toString() {
409    return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
410  }
411
412  public static RulesProfile create(String name, String language) {
413    return new RulesProfile().setName(name).setLanguage(language);
414  }
415
416  public static RulesProfile create() {
417    return new RulesProfile();
418  }
419}