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