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