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.annotation.Nullable;
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    public class RulesProfile implements Cloneable {
045    
046      /**
047       * Name of the default profile "Sonar Way"
048       * @deprecated in 4.2. Use your own constant.
049       */
050      @Deprecated
051      public static final String SONAR_WAY_NAME = "Sonar way";
052    
053      /**
054       * Name of the default java profile "Sonar way with Findbugs"
055       * @deprecated in 4.2. Use your own constant.
056       */
057      @Deprecated
058      public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
059    
060      /**
061       * Name of the default java profile "Sun checks"
062       * @deprecated in 4.2. Use your own constant.
063       */
064      @Deprecated
065      public static final String SUN_CONVENTIONS_NAME = "Sun checks";
066    
067      private String name;
068      private Boolean defaultProfile = Boolean.FALSE;
069      private String language;
070      private String parentName;
071      private List<ActiveRule> activeRules = Lists.newArrayList();
072    
073      /**
074       * @deprecated use the factory method create()
075       */
076      @Deprecated
077      public RulesProfile() {
078      }
079    
080      /**
081       * @deprecated since 2.3. Use the factory method create()
082       */
083      @Deprecated
084      public RulesProfile(String name, String language) {
085        this.name = name;
086        this.language = language;
087        this.activeRules = Lists.newArrayList();
088      }
089    
090      /**
091       * @deprecated since 2.3. Use the factory method create()
092       */
093      @Deprecated
094      public RulesProfile(String name, String language, boolean defaultProfile, /* kept for backward-compatibility */boolean provided) {
095        this(name, language);
096        this.defaultProfile = defaultProfile;
097      }
098    
099      public Integer getId() {
100        return null;
101      }
102    
103      /**
104       * @return the profile name, unique by language.
105       */
106      public String getName() {
107        return name;
108      }
109    
110      /**
111       * Set the profile name.
112       */
113      public RulesProfile setName(String s) {
114        this.name = s;
115        return this;
116      }
117    
118      /**
119       * @deprecated profile versioning is dropped in 4.4. Always returns -1.
120       */
121      @Deprecated
122      public int getVersion() {
123        return -1;
124      }
125    
126      /**
127       * @deprecated profile versioning is dropped in 4.4. Always returns -1.
128       */
129      @Deprecated
130      public RulesProfile setVersion(int version) {
131        // ignore
132        return this;
133      }
134    
135      /**
136       * @deprecated profile versioning is dropped in 4.4. Always returns -1.
137       */
138      @CheckForNull
139      @Deprecated
140      public Boolean getUsed() {
141        return null;
142      }
143    
144      /**
145       * @deprecated profile versioning is dropped in 4.4. Always returns -1.
146       */
147      @Deprecated
148      public RulesProfile setUsed(Boolean used) {
149        return this;
150      }
151    
152      /**
153       * @return the list of active rules
154       */
155      public List<ActiveRule> getActiveRules() {
156        return getActiveRules(false);
157      }
158    
159      /**
160       * @return the list of active rules
161       */
162      public List<ActiveRule> getActiveRules(boolean acceptDisabledRules) {
163        if (acceptDisabledRules) {
164          return activeRules;
165        }
166        List<ActiveRule> result = Lists.newArrayList();
167        for (ActiveRule activeRule : activeRules) {
168          if (activeRule.isEnabled()) {
169            result.add(activeRule);
170          }
171        }
172        return result;
173      }
174    
175      public RulesProfile removeActiveRule(ActiveRule activeRule) {
176        activeRules.remove(activeRule);
177        return this;
178      }
179    
180      public RulesProfile addActiveRule(ActiveRule activeRule) {
181        activeRules.add(activeRule);
182        return this;
183      }
184    
185      /**
186       * Set the list of active rules
187       */
188      public void setActiveRules(List<ActiveRule> activeRules) {
189        this.activeRules = activeRules;
190      }
191    
192      /**
193       * @return whether this is the default profile for the language
194       */
195      public Boolean getDefaultProfile() {
196        return defaultProfile;
197      }
198    
199      /**
200       * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when auditing a
201       * project.
202       */
203      public void setDefaultProfile(Boolean b) {
204        this.defaultProfile = b;
205      }
206    
207      /**
208       * @deprecated since 3.3 not replaced
209       */
210      @Deprecated
211      public Boolean getProvided() {
212        return false;
213      }
214    
215      /**
216       * @deprecated since 3.3 not replaced
217       */
218      @Deprecated
219      public void setProvided(Boolean b) {
220      }
221    
222      /**
223       * @deprecated since 3.3. Always return true.
224       */
225      @Deprecated
226      public Boolean getEnabled() {
227        return Boolean.TRUE;
228      }
229    
230      /**
231       * @deprecated since 3.3. Always return true.
232       */
233      @Deprecated
234      public boolean isEnabled() {
235        return true;
236      }
237    
238      /**
239       * @deprecated since 3.3.
240       */
241      @Deprecated
242      public RulesProfile setEnabled(Boolean b) {
243        throw new UnsupportedOperationException("The field RulesProfile#enabled is not supported since 3.3.");
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      @CheckForNull
267      public String getParentName() {
268        return parentName;
269      }
270    
271      /**
272       * For internal use only.
273       *
274       * @since 2.5
275       */
276      public void setParentName(String parentName) {
277        this.parentName = parentName;
278      }
279    
280      /**
281       * Note: disabled rules are excluded.
282       *
283       * @return the list of active rules for a given severity
284       */
285      public List<ActiveRule> getActiveRules(RulePriority severity) {
286        List<ActiveRule> result = Lists.newArrayList();
287        for (ActiveRule activeRule : activeRules) {
288          if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
289            result.add(activeRule);
290          }
291        }
292        return result;
293      }
294    
295      /**
296       * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
297       */
298      @Deprecated
299      public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
300        return getActiveRulesByRepository(repositoryKey);
301      }
302    
303      /**
304       * Get the active rules of a specific repository.
305       * Only enabled rules are selected. Disabled rules are excluded.
306       */
307      public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
308        List<ActiveRule> result = Lists.newArrayList();
309        for (ActiveRule activeRule : activeRules) {
310          if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
311            result.add(activeRule);
312          }
313        }
314        return result;
315      }
316    
317      /**
318       * Note: disabled rules are excluded.
319       *
320       * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
321       */
322      @CheckForNull
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      @CheckForNull
336      public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
337        for (ActiveRule activeRule : activeRules) {
338          if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
339            return activeRule;
340          }
341        }
342        return null;
343      }
344    
345      /**
346       * Note: disabled rules are excluded.
347       */
348      @CheckForNull
349      public ActiveRule getActiveRule(Rule rule) {
350        return getActiveRule(rule.getRepositoryKey(), rule.getKey());
351      }
352    
353      /**
354       * @param optionalSeverity if null, then the default rule severity is used
355       */
356      public ActiveRule activateRule(final Rule rule, @Nullable RulePriority optionalSeverity) {
357        if (Iterables.any(activeRules, new Predicate<ActiveRule>() {
358          @Override
359          public boolean apply(ActiveRule input) {
360            return input.getRule().equals(rule);
361          }
362        })) {
363          throw MessageException.of(String.format(
364            "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.",
365            getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey()));
366        }
367        ActiveRule activeRule = new ActiveRule();
368        activeRule.setRule(rule);
369        activeRule.setRulesProfile(this);
370        activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
371        activeRules.add(activeRule);
372        return activeRule;
373      }
374    
375      @Override
376      public boolean equals(Object obj) {
377        if (!(obj instanceof RulesProfile)) {
378          return false;
379        }
380        if (this == obj) {
381          return true;
382        }
383        RulesProfile other = (RulesProfile) obj;
384        return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
385      }
386    
387      @Override
388      public int hashCode() {
389        return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
390      }
391    
392      @Override
393      public Object clone() {
394        RulesProfile clone = RulesProfile.create(getName(), getLanguage());
395        clone.setDefaultProfile(getDefaultProfile());
396        clone.setParentName(getParentName());
397        if (activeRules != null && !activeRules.isEmpty()) {
398          clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
399            public Object transform(Object input) {
400              return ((ActiveRule) input).clone();
401            }
402          })));
403        }
404        return clone;
405      }
406    
407      @Override
408      public String toString() {
409        return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
410      }
411    
412      public static RulesProfile create(String name, String language) {
413        return new RulesProfile().setName(name).setLanguage(language);
414      }
415    
416      public static RulesProfile create() {
417        return new RulesProfile();
418      }
419    }