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 = "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       * @return the list of active rules for a given severity
310       */
311      public List<ActiveRule> getActiveRules(RulePriority severity) {
312        List<ActiveRule> result = Lists.newArrayList();
313        for (ActiveRule activeRule : activeRules) {
314          if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
315            result.add(activeRule);
316          }
317        }
318        return result;
319      }
320    
321      /**
322       * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
323       */
324      @Deprecated
325      public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
326        return getActiveRulesByRepository(repositoryKey);
327      }
328    
329      /**
330       * Get the active rules of a specific repository.
331       * Only enabled rules are selected. Disabled rules are excluded.
332       */
333      public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
334        List<ActiveRule> result = Lists.newArrayList();
335        for (ActiveRule activeRule : activeRules) {
336          if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
337            result.add(activeRule);
338          }
339        }
340        return result;
341      }
342    
343      /**
344       * Note: disabled rules are excluded.
345       * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
346       */
347      public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
348        for (ActiveRule activeRule : activeRules) {
349          if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
350            return activeRule;
351          }
352        }
353        return null;
354      }
355    
356      /**
357       * Note: disabled rules are excluded.
358       */
359      public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
360        for (ActiveRule activeRule : activeRules) {
361          if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
362            return activeRule;
363          }
364        }
365        return null;
366      }
367    
368      /**
369       * Note: disabled rules are excluded.
370       */
371    
372      public ActiveRule getActiveRule(Rule rule) {
373        return getActiveRule(rule.getRepositoryKey(), rule.getKey());
374      }
375    
376      /**
377       * @param optionalSeverity if null, then the default rule severity is used
378       */
379      public ActiveRule activateRule(Rule rule, RulePriority optionalSeverity) {
380        ActiveRule activeRule = new ActiveRule();
381        activeRule.setRule(rule);
382        activeRule.setRulesProfile(this);
383        activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
384        activeRules.add(activeRule);
385        return activeRule;
386      }
387    
388      @Override
389      public boolean equals(Object obj) {
390        if (!(obj instanceof RulesProfile)) {
391          return false;
392        }
393        if (this == obj) {
394          return true;
395        }
396        RulesProfile other = (RulesProfile) obj;
397        return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
398      }
399    
400      @Override
401      public int hashCode() {
402        return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
403      }
404    
405      @Override
406      public Object clone() {
407        RulesProfile clone = RulesProfile.create(getName(), getLanguage());
408        clone.setDefaultProfile(getDefaultProfile());
409        clone.setProvided(getProvided());
410        clone.setParentName(getParentName());
411        if (CollectionUtils.isNotEmpty(activeRules)) {
412          clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
413            public Object transform(Object input) {
414              return ((ActiveRule) input).clone();
415            }
416          })));
417        }
418        if (CollectionUtils.isNotEmpty(getAlerts())) {
419          clone.setAlerts(new ArrayList<Alert>(CollectionUtils.collect(getAlerts(), new Transformer() {
420            public Object transform(Object input) {
421              return ((Alert) input).clone();
422            }
423          })));
424        }
425        if (CollectionUtils.isNotEmpty(getProjects())) {
426          clone.setProjects(new ArrayList<ResourceModel>(CollectionUtils.collect(getProjects(), new Transformer() {
427            public Object transform(Object input) {
428              return ((ResourceModel) input).clone();
429            }
430          })));
431        }
432        return clone;
433      }
434    
435      @Override
436      public String toString() {
437        return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
438      }
439    
440      public static RulesProfile create(String name, String language) {
441        return new RulesProfile().setName(name).setLanguage(language);
442      }
443    
444      public static RulesProfile create() {
445        return new RulesProfile();
446      }
447    }