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