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