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