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