001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar 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 * Sonar 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
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
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   * @return
232   * @deprecated since 3.3. Always return true.
233   */
234  @Deprecated
235  public Boolean getEnabled() {
236    return Boolean.TRUE;
237  }
238
239  /**
240   * @return
241   * @deprecated since 3.3. Always return true.
242   */
243  @Deprecated
244  public boolean isEnabled() {
245    return true;
246  }
247
248  /**
249   * @return
250   * @deprecated since 3.3.
251   */
252  @Deprecated
253  public RulesProfile setEnabled(Boolean b) {
254    throw new UnsupportedOperationException("The field RulesProfile#enabled is not supported since 3.3.");
255  }
256
257  /**
258   * @return the profile language
259   */
260  public String getLanguage() {
261    return language;
262  }
263
264  /**
265   * Set the profile language
266   */
267  public RulesProfile setLanguage(String s) {
268    this.language = s;
269    return this;
270  }
271
272  /**
273   * For internal use only.
274   *
275   * @since 2.5
276   */
277  public String getParentName() {
278    return parentName;
279  }
280
281  /**
282   * For internal use only.
283   *
284   * @since 2.5
285   */
286  public void setParentName(String parentName) {
287    this.parentName = parentName;
288  }
289
290  /**
291   * @return the list of alerts defined in the profile
292   */
293  public List<Alert> getAlerts() {
294    return alerts;
295  }
296
297  /**
298   * Sets the list of alerts for the profile
299   */
300  public void setAlerts(List<Alert> alerts) {
301    this.alerts = alerts;
302  }
303
304  /**
305   * Note: disabled rules are excluded.
306   *
307   * @return the list of active rules for a given severity
308   */
309  public List<ActiveRule> getActiveRules(RulePriority severity) {
310    List<ActiveRule> result = Lists.newArrayList();
311    for (ActiveRule activeRule : activeRules) {
312      if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
313        result.add(activeRule);
314      }
315    }
316    return result;
317  }
318
319  /**
320   * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
321   */
322  @Deprecated
323  public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
324    return getActiveRulesByRepository(repositoryKey);
325  }
326
327  /**
328   * Get the active rules of a specific repository.
329   * Only enabled rules are selected. Disabled rules are excluded.
330   */
331  public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
332    List<ActiveRule> result = Lists.newArrayList();
333    for (ActiveRule activeRule : activeRules) {
334      if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
335        result.add(activeRule);
336      }
337    }
338    return result;
339  }
340
341  /**
342   * Note: disabled rules are excluded.
343   *
344   * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
345   */
346  public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
347    for (ActiveRule activeRule : activeRules) {
348      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
349        return activeRule;
350      }
351    }
352    return null;
353  }
354
355  /**
356   * Note: disabled rules are excluded.
357   */
358  public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
359    for (ActiveRule activeRule : activeRules) {
360      if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
361        return activeRule;
362      }
363    }
364    return null;
365  }
366
367  /**
368   * Note: disabled rules are excluded.
369   */
370
371  public ActiveRule getActiveRule(Rule rule) {
372    return getActiveRule(rule.getRepositoryKey(), rule.getKey());
373  }
374
375  /**
376   * @param optionalSeverity if null, then the default rule severity is used
377   */
378  public ActiveRule activateRule(Rule rule, RulePriority optionalSeverity) {
379    ActiveRule activeRule = new ActiveRule();
380    activeRule.setRule(rule);
381    activeRule.setRulesProfile(this);
382    activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
383    activeRules.add(activeRule);
384    return activeRule;
385  }
386
387  @Override
388  public boolean equals(Object obj) {
389    if (!(obj instanceof RulesProfile)) {
390      return false;
391    }
392    if (this == obj) {
393      return true;
394    }
395    RulesProfile other = (RulesProfile) obj;
396    return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
397  }
398
399  @Override
400  public int hashCode() {
401    return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
402  }
403
404  @Override
405  public Object clone() {
406    RulesProfile clone = RulesProfile.create(getName(), getLanguage());
407    clone.setDefaultProfile(getDefaultProfile());
408    clone.setParentName(getParentName());
409    if (activeRules != null && !activeRules.isEmpty()) {
410      clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
411        public Object transform(Object input) {
412          return ((ActiveRule) input).clone();
413        }
414      })));
415    }
416    if (CollectionUtils.isNotEmpty(getAlerts())) {
417      clone.setAlerts(new ArrayList<Alert>(CollectionUtils.collect(getAlerts(), new Transformer() {
418        public Object transform(Object input) {
419          return ((Alert) input).clone();
420        }
421      })));
422    }
423    return clone;
424  }
425
426  @Override
427  public String toString() {
428    return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
429  }
430
431  public static RulesProfile create(String name, String language) {
432    return new RulesProfile().setName(name).setLanguage(language);
433  }
434
435  public static RulesProfile create() {
436    return new RulesProfile();
437  }
438}