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