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.apache.commons.lang.builder.ReflectionToStringBuilder;
029    import org.apache.commons.lang.builder.ToStringStyle;
030    import org.sonar.api.database.model.ResourceModel;
031    import org.sonar.api.rules.ActiveRule;
032    import org.sonar.api.rules.Rule;
033    import org.sonar.api.rules.RulePriority;
034    
035    import javax.persistence.*;
036    import java.util.ArrayList;
037    import java.util.List;
038    
039    /**
040     * This class is badly named. It should be "QualityProfile". Indeed it does not relate only to rules but to metric thresholds too.
041     */
042    @Entity
043    @Table(name = "rules_profiles")
044    public class RulesProfile implements Cloneable {
045    
046      /**
047       * Name of the default profile "Sonar Way"
048       */
049      public static final String SONAR_WAY_NAME = "Sonar way";
050    
051      /**
052       * Name of the default java profile "Sonar way with Findbugs"
053       */
054      public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
055    
056      /**
057       * Name of the default java profile "Sun checks"
058       */
059      public static final String SUN_CONVENTIONS_NAME = "Sun checks";
060    
061      @Id
062      @Column(name = "id")
063      @GeneratedValue
064      private Integer id;
065    
066      @Column(name = "name", updatable = true, nullable = false)
067      private String name;
068    
069      @Column(name = "version", updatable = true, nullable = false)
070      private int version = 1;
071    
072      @Column(name = "default_profile", updatable = true, nullable = false)
073      private Boolean defaultProfile = Boolean.FALSE;
074    
075      @Column(name = "provided", updatable = true, nullable = false)
076      private Boolean provided = Boolean.FALSE;
077    
078      @Column(name = "enabled", updatable = true, nullable = false)
079      private Boolean enabled = Boolean.TRUE;
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)
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      @OneToMany(mappedBy = "rulesProfile", fetch = FetchType.LAZY)
097      private List<ResourceModel> projects = Lists.newArrayList();
098    
099      /**
100       * @deprecated use the factory method create()
101       */
102      @Deprecated
103      public RulesProfile() {
104      }
105    
106      /**
107       * @deprecated since 2.3. Use the factory method create()
108       */
109      @Deprecated
110      public RulesProfile(String name, String language) {
111        this.name = name;
112        this.language = language;
113        this.activeRules = Lists.newArrayList();
114        this.alerts = Lists.newArrayList();
115        this.projects = Lists.newArrayList();
116      }
117    
118      /**
119       * @deprecated since 2.3. Use the factory method create()
120       */
121      @Deprecated
122      public RulesProfile(String name, String language, boolean defaultProfile, boolean provided) {
123        this(name, language);
124        this.defaultProfile = defaultProfile;
125        this.provided = provided;
126      }
127    
128      public Integer getId() {
129        return id;
130      }
131    
132      /**
133       * @return the profile name, unique by language.
134       */
135      public String getName() {
136        return name;
137      }
138    
139      /**
140       * Set the profile name.
141       */
142      public RulesProfile setName(String s) {
143        this.name = s;
144        return this;
145      }
146      
147      public int getVersion() {
148        return version;
149      }
150      
151      public RulesProfile setVersion(int version) {
152        this.version = version;
153        return this;
154      }
155      
156      public Boolean getUsed() {
157        return used;
158      }
159      
160      public RulesProfile setUsed(Boolean used) {
161        this.used = used;
162        return this;
163      }
164    
165      /**
166       * @return the list of active rules
167       */
168      public List<ActiveRule> getActiveRules() {
169        return getActiveRules(false);
170      }
171    
172      /**
173       * @return the list of active rules
174       */
175      public List<ActiveRule> getActiveRules(boolean acceptDisabledRules) {
176        if (acceptDisabledRules) {
177          return activeRules;
178        }
179        List<ActiveRule> result = Lists.newArrayList();
180        for (ActiveRule activeRule : activeRules) {
181          if (activeRule.isEnabled()) {
182            result.add(activeRule);
183          }
184        }
185        return result;
186      }
187    
188      public RulesProfile removeActiveRule(ActiveRule activeRule) {
189        activeRules.remove(activeRule);
190        return this;
191      }
192    
193      public RulesProfile addActiveRule(ActiveRule activeRule) {
194        activeRules.add(activeRule);
195        return this;
196      }
197    
198      /**
199       * Set the list of active rules
200       */
201      public void setActiveRules(List<ActiveRule> activeRules) {
202        this.activeRules = activeRules;
203      }
204    
205      /**
206       * @return whether this is the default profile for the language
207       */
208      public Boolean getDefaultProfile() {
209        return defaultProfile;
210      }
211    
212      /**
213       * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when auditing a
214       * project.
215       */
216      public void setDefaultProfile(Boolean b) {
217        this.defaultProfile = b;
218      }
219    
220      /**
221       * @return whether the profile is defined in a plugin. Provided profiles are automatically restored during server startup and can not be
222       *         updated by end users.
223       */
224      public Boolean getProvided() {
225        return provided;
226      }
227    
228      /**
229       * Set whether the profile is provided by a plugin
230       */
231      public void setProvided(Boolean b) {
232        this.provided = b;
233      }
234    
235      public Boolean getEnabled() {
236        return enabled;
237      }
238    
239      public boolean isEnabled() {
240        return enabled == Boolean.TRUE;
241      }
242    
243      public RulesProfile setEnabled(Boolean b) {
244        this.enabled = b;
245        return this;
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       * @return the list of projects attached to the profile
297       */
298      public List<ResourceModel> getProjects() {
299        return projects;
300      }
301    
302      /**
303       * Sets the list of projects attached to the profile
304       */
305      public void setProjects(List<ResourceModel> projects) {
306        this.projects = projects;
307      }
308    
309      /**
310       * Note: disabled rules are excluded.
311       * @return the list of active rules for a given severity
312       */
313      public List<ActiveRule> getActiveRules(RulePriority severity) {
314        List<ActiveRule> result = Lists.newArrayList();
315        for (ActiveRule activeRule : activeRules) {
316          if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
317            result.add(activeRule);
318          }
319        }
320        return result;
321      }
322    
323      /**
324       * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
325       */
326      @Deprecated
327      public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
328        return getActiveRulesByRepository(repositoryKey);
329      }
330    
331      /**
332       * Get the active rules of a specific repository.
333       * Only enabled rules are selected. Disabled rules are excluded.
334       */
335      public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
336        List<ActiveRule> result = Lists.newArrayList();
337        for (ActiveRule activeRule : activeRules) {
338          if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
339            result.add(activeRule);
340          }
341        }
342        return result;
343      }
344    
345      /**
346       * Note: disabled rules are excluded.
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    }