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