001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program 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 * This program 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 */ 020package org.sonar.api.profiles; 021 022import com.google.common.base.Predicate; 023import com.google.common.collect.Iterables; 024 025import java.util.ArrayList; 026import java.util.List; 027 028import javax.annotation.CheckForNull; 029import javax.annotation.Nullable; 030 031import org.apache.commons.collections.CollectionUtils; 032import org.apache.commons.collections.Transformer; 033import org.apache.commons.lang.StringUtils; 034import org.apache.commons.lang.builder.EqualsBuilder; 035import org.apache.commons.lang.builder.HashCodeBuilder; 036import org.sonar.api.rules.ActiveRule; 037import org.sonar.api.rules.Rule; 038import org.sonar.api.rules.RulePriority; 039import org.sonar.api.utils.MessageException; 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 */ 044public 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 = new ArrayList<>(); 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 = new ArrayList<>(); 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 null. 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 = new ArrayList<>(); 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 = new ArrayList<>(); 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 = new ArrayList<>(); 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 MatchRule(rule))) { 311 throw MessageException.of(String.format( 312 "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.", 313 getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey())); 314 } 315 ActiveRule activeRule = new ActiveRule(); 316 activeRule.setRule(rule); 317 activeRule.setRulesProfile(this); 318 activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity); 319 activeRules.add(activeRule); 320 return activeRule; 321 } 322 323 @Override 324 public boolean equals(Object obj) { 325 if (!(obj instanceof RulesProfile)) { 326 return false; 327 } 328 if (this == obj) { 329 return true; 330 } 331 RulesProfile other = (RulesProfile) obj; 332 return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals(); 333 } 334 335 @Override 336 public int hashCode() { 337 return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode(); 338 } 339 340 @Override 341 public Object clone() { 342 RulesProfile clone = RulesProfile.create(getName(), getLanguage()); 343 clone.setDefaultProfile(getDefaultProfile()); 344 clone.setParentName(getParentName()); 345 if (activeRules != null && !activeRules.isEmpty()) { 346 clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() { 347 @Override 348 public Object transform(Object input) { 349 return ((ActiveRule) input).clone(); 350 } 351 }))); 352 } 353 return clone; 354 } 355 356 @Override 357 public String toString() { 358 return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString(); 359 } 360 361 public static RulesProfile create(String name, String language) { 362 return new RulesProfile().setName(name).setLanguage(language); 363 } 364 365 public static RulesProfile create() { 366 return new RulesProfile(); 367 } 368 369 private static class MatchRule implements Predicate<ActiveRule> { 370 private final Rule rule; 371 372 public MatchRule(Rule rule) { 373 this.rule = rule; 374 } 375 376 @Override 377 public boolean apply(ActiveRule input) { 378 return input.getRule().equals(rule); 379 } 380 } 381}