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       * @return the profile language
209       */
210      public String getLanguage() {
211        return language;
212      }
213    
214      /**
215       * Set the profile language
216       */
217      public RulesProfile setLanguage(String s) {
218        this.language = s;
219        return this;
220      }
221    
222      /**
223       * For internal use only.
224       *
225       * @since 2.5
226       */
227      @CheckForNull
228      public String getParentName() {
229        return parentName;
230      }
231    
232      /**
233       * For internal use only.
234       *
235       * @since 2.5
236       */
237      public void setParentName(String parentName) {
238        this.parentName = parentName;
239      }
240    
241      /**
242       * Note: disabled rules are excluded.
243       *
244       * @return the list of active rules for a given severity
245       */
246      public List<ActiveRule> getActiveRules(RulePriority severity) {
247        List<ActiveRule> result = Lists.newArrayList();
248        for (ActiveRule activeRule : activeRules) {
249          if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
250            result.add(activeRule);
251          }
252        }
253        return result;
254      }
255    
256      /**
257       * Get the active rules of a specific repository.
258       * Only enabled rules are selected. Disabled rules are excluded.
259       */
260      public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
261        List<ActiveRule> result = Lists.newArrayList();
262        for (ActiveRule activeRule : activeRules) {
263          if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
264            result.add(activeRule);
265          }
266        }
267        return result;
268      }
269    
270      /**
271       * Note: disabled rules are excluded.
272       *
273       * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
274       */
275      @CheckForNull
276      public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
277        for (ActiveRule activeRule : activeRules) {
278          if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
279            return activeRule;
280          }
281        }
282        return null;
283      }
284    
285      /**
286       * Note: disabled rules are excluded.
287       */
288      @CheckForNull
289      public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
290        for (ActiveRule activeRule : activeRules) {
291          if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
292            return activeRule;
293          }
294        }
295        return null;
296      }
297    
298      /**
299       * Note: disabled rules are excluded.
300       */
301      @CheckForNull
302      public ActiveRule getActiveRule(Rule rule) {
303        return getActiveRule(rule.getRepositoryKey(), rule.getKey());
304      }
305    
306      /**
307       * @param optionalSeverity if null, then the default rule severity is used
308       */
309      public ActiveRule activateRule(final Rule rule, @Nullable RulePriority optionalSeverity) {
310        if (Iterables.any(activeRules, new Predicate<ActiveRule>() {
311          @Override
312          public boolean apply(ActiveRule input) {
313            return input.getRule().equals(rule);
314          }
315        })) {
316          throw MessageException.of(String.format(
317            "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.",
318            getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey()));
319        }
320        ActiveRule activeRule = new ActiveRule();
321        activeRule.setRule(rule);
322        activeRule.setRulesProfile(this);
323        activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
324        activeRules.add(activeRule);
325        return activeRule;
326      }
327    
328      @Override
329      public boolean equals(Object obj) {
330        if (!(obj instanceof RulesProfile)) {
331          return false;
332        }
333        if (this == obj) {
334          return true;
335        }
336        RulesProfile other = (RulesProfile) obj;
337        return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
338      }
339    
340      @Override
341      public int hashCode() {
342        return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
343      }
344    
345      @Override
346      public Object clone() {
347        RulesProfile clone = RulesProfile.create(getName(), getLanguage());
348        clone.setDefaultProfile(getDefaultProfile());
349        clone.setParentName(getParentName());
350        if (activeRules != null && !activeRules.isEmpty()) {
351          clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
352            @Override
353            public Object transform(Object input) {
354              return ((ActiveRule) input).clone();
355            }
356          })));
357        }
358        return clone;
359      }
360    
361      @Override
362      public String toString() {
363        return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
364      }
365    
366      public static RulesProfile create(String name, String language) {
367        return new RulesProfile().setName(name).setLanguage(language);
368      }
369    
370      public static RulesProfile create() {
371        return new RulesProfile();
372      }
373    }