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 */ 020 021 package org.sonar.api.rules; 022 023 import com.google.common.base.Joiner; 024 import com.google.common.collect.ImmutableSet; 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.commons.lang.builder.EqualsBuilder; 027 import org.apache.commons.lang.builder.HashCodeBuilder; 028 import org.apache.commons.lang.builder.ToStringBuilder; 029 import org.apache.commons.lang.builder.ToStringStyle; 030 import org.sonar.api.database.DatabaseProperties; 031 import org.sonar.api.rule.RuleKey; 032 import org.sonar.api.utils.SonarException; 033 import org.sonar.check.Cardinality; 034 035 import javax.annotation.CheckForNull; 036 import javax.annotation.Nullable; 037 import javax.persistence.*; 038 039 import java.util.ArrayList; 040 import java.util.Date; 041 import java.util.List; 042 import java.util.Set; 043 044 @Entity 045 @Table(name = "rules") 046 public final class Rule { 047 048 /** 049 * @since 3.6 050 */ 051 public static final String STATUS_BETA = "BETA"; 052 /** 053 * @since 3.6 054 */ 055 public static final String STATUS_DEPRECATED = "DEPRECATED"; 056 /** 057 * @since 3.6 058 */ 059 public static final String STATUS_READY = "READY"; 060 061 /** 062 * For internal use only. 063 * @since 3.6 064 */ 065 public static final String STATUS_REMOVED = "REMOVED"; 066 067 /** 068 * List of available status 069 * @since 3.6 070 */ 071 private static final Set<String> STATUS_LIST = ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED); 072 073 074 @Id 075 @Column(name = "id") 076 @GeneratedValue 077 private Integer id; 078 079 /** 080 * The default priority given to a rule if not explicitly set 081 */ 082 public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR; 083 084 @Column(name = "name", updatable = true, nullable = true, length = 200) 085 private String name; 086 087 @Column(name = "plugin_rule_key", updatable = false, nullable = true, length = 200) 088 private String key; 089 090 @Column(name = "plugin_config_key", updatable = true, nullable = true, length = 500) 091 private String configKey; 092 093 @Column(name = "priority", updatable = true, nullable = true) 094 @Enumerated(EnumType.ORDINAL) 095 private RulePriority priority = DEFAULT_PRIORITY; 096 097 @Column(name = "description", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE) 098 private String description; 099 100 @Column(name = "plugin_name", updatable = true, nullable = false) 101 private String pluginName; 102 103 @Enumerated(EnumType.STRING) 104 @Column(name = "cardinality", updatable = true, nullable = false) 105 private Cardinality cardinality = Cardinality.SINGLE; 106 107 @Column(name = "status", updatable = true, nullable = true) 108 private String status = STATUS_READY; 109 110 @Column(name = "language", updatable = true, nullable = true) 111 private String language; 112 113 @ManyToOne(fetch = FetchType.EAGER) 114 @JoinColumn(name = "parent_id", updatable = true, nullable = true) 115 private Rule parent = null; 116 117 @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) 118 @OneToMany(mappedBy = "rule") 119 private List<RuleParam> params = new ArrayList<RuleParam>(); 120 121 @Temporal(TemporalType.TIMESTAMP) 122 @Column(name = "created_at", updatable = true, nullable = true) 123 private Date createdAt; 124 125 @Temporal(TemporalType.TIMESTAMP) 126 @Column(name = "updated_at", updatable = true, nullable = true) 127 private Date updatedAt; 128 129 /** 130 * @deprecated since 2.3. Use the factory method {@link #create()} 131 */ 132 @Deprecated 133 public Rule() { 134 // TODO reduce visibility to packaete 135 } 136 137 /** 138 * Creates rule with minimum set of info 139 * 140 * @param pluginName the plugin name indicates which plugin the rule belongs to 141 * @param key the key should be unique within a plugin, but it is even more careful for the time being that it is unique across the 142 * application 143 * @deprecated since 2.3. Use the factory method {@link #create()} 144 */ 145 @Deprecated 146 public Rule(String pluginName, String key) { 147 this.pluginName = pluginName; 148 this.key = key; 149 this.configKey = key; 150 } 151 152 /** 153 * Creates a fully qualified rule 154 * 155 * @param pluginKey the plugin the rule belongs to 156 * @param key the key should be unique within a plugin, but it is even more careful for the time being that it is unique across the 157 * application 158 * @param name the name displayed in the UI 159 * @param rulesCategory the ISO category the rule belongs to 160 * @param severity this is the severity associated to the rule 161 * @deprecated since 2.3. Use the factory method {@link #create()} 162 */ 163 @Deprecated 164 public Rule(String pluginKey, String key, String name, RulesCategory rulesCategory, RulePriority severity) { 165 setName(name); 166 this.key = key; 167 this.configKey = key; 168 this.priority = severity; 169 this.pluginName = pluginKey; 170 } 171 172 /** 173 * @deprecated since 2.3. Use the factory method {@link #create()} 174 */ 175 @Deprecated 176 public Rule(String name, String key, RulesCategory rulesCategory, String pluginName, String description) { 177 this(); 178 setName(name); 179 this.key = key; 180 this.configKey = key; 181 this.pluginName = pluginName; 182 this.description = description; 183 } 184 185 /** 186 * @deprecated since 2.3. Use the factory method {@link #create()} 187 */ 188 @Deprecated 189 public Rule(String name, String key, String configKey, RulesCategory rulesCategory, String pluginName, String description) { 190 this(); 191 setName(name); 192 this.key = key; 193 this.configKey = configKey; 194 this.pluginName = pluginName; 195 this.description = description; 196 } 197 198 public Integer getId() { 199 return id; 200 } 201 202 /** 203 * @deprecated since 2.3. visibility should be decreased to protected or package 204 */ 205 @Deprecated 206 public void setId(Integer id) { 207 this.id = id; 208 } 209 210 @CheckForNull 211 public String getName() { 212 return name; 213 } 214 215 /** 216 * Sets the rule name 217 */ 218 public Rule setName(@Nullable String name) { 219 this.name = removeNewLineCharacters(name); 220 return this; 221 } 222 223 public String getKey() { 224 return key; 225 } 226 227 /** 228 * Sets the rule key 229 */ 230 public Rule setKey(String key) { 231 this.key = key; 232 return this; 233 } 234 235 /** 236 * @deprecated since 2.5 use {@link #getRepositoryKey()} instead 237 */ 238 @Deprecated 239 public String getPluginName() { 240 return pluginName; 241 } 242 243 /** 244 * @deprecated since 2.5 use {@link #setRepositoryKey(String)} instead 245 */ 246 @Deprecated 247 public Rule setPluginName(String pluginName) { 248 this.pluginName = pluginName; 249 return this; 250 } 251 252 public String getConfigKey() { 253 return configKey; 254 } 255 256 /** 257 * Sets the configuration key 258 */ 259 public Rule setConfigKey(String configKey) { 260 this.configKey = configKey; 261 return this; 262 } 263 264 public String getDescription() { 265 return description; 266 } 267 268 /** 269 * Sets the rule description 270 */ 271 public Rule setDescription(String description) { 272 this.description = StringUtils.strip(description); 273 return this; 274 } 275 276 /** 277 * @deprecated in 3.6. Replaced by {@link #setStatus(String status)}. 278 */ 279 @Deprecated 280 public Rule setEnabled(Boolean enabled) { 281 throw new UnsupportedOperationException("No more supported since version 3.6."); 282 } 283 284 public Boolean isEnabled() { 285 return !"REMOVED".equals(status); 286 } 287 288 public List<RuleParam> getParams() { 289 return params; 290 } 291 292 public RuleParam getParam(String key) { 293 for (RuleParam param : params) { 294 if (StringUtils.equals(key, param.getKey())) { 295 return param; 296 } 297 } 298 return null; 299 } 300 301 /** 302 * Sets the rule parameters 303 */ 304 public Rule setParams(List<RuleParam> params) { 305 this.params.clear(); 306 for (RuleParam param : params) { 307 param.setRule(this); 308 this.params.add(param); 309 } 310 return this; 311 } 312 313 public RuleParam createParameter() { 314 RuleParam parameter = new RuleParam() 315 .setRule(this); 316 params.add(parameter); 317 return parameter; 318 } 319 320 public RuleParam createParameter(String key) { 321 RuleParam parameter = new RuleParam() 322 .setKey(key) 323 .setRule(this); 324 params.add(parameter); 325 return parameter; 326 } 327 328 /** 329 * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2007 330 */ 331 @Deprecated 332 public Integer getCategoryId() { 333 return null; 334 } 335 336 /** 337 * @since 2.5 338 */ 339 public RulePriority getSeverity() { 340 return priority; 341 } 342 343 /** 344 * @param severity severity to set, if null, uses the default priority. 345 * @since 2.5 346 */ 347 public Rule setSeverity(RulePriority severity) { 348 if (severity == null) { 349 this.priority = DEFAULT_PRIORITY; 350 } else { 351 this.priority = severity; 352 } 353 return this; 354 } 355 356 /** 357 * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829 358 */ 359 @Deprecated 360 public RulePriority getPriority() { 361 return priority; 362 } 363 364 /** 365 * Sets the rule priority. If null, uses the default priority 366 * 367 * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829 368 */ 369 @Deprecated 370 public Rule setPriority(RulePriority priority) { 371 return setSeverity(priority); 372 } 373 374 public String getRepositoryKey() { 375 return pluginName; 376 } 377 378 public Rule setRepositoryKey(String s) { 379 this.pluginName = s; 380 return this; 381 } 382 383 public Rule setUniqueKey(String repositoryKey, String key) { 384 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key); 385 } 386 387 public Cardinality getCardinality() { 388 return cardinality; 389 } 390 391 public Rule setCardinality(Cardinality c) { 392 this.cardinality = c; 393 return this; 394 } 395 396 public Rule getParent() { 397 return parent; 398 } 399 400 public Rule setParent(Rule parent) { 401 this.parent = parent; 402 return this; 403 } 404 405 /** 406 * @since 3.6 407 */ 408 public String getStatus() { 409 return status; 410 } 411 412 /** 413 * @since 3.6 414 */ 415 public Rule setStatus(String status) { 416 if (!STATUS_LIST.contains(status)) { 417 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST)); 418 } 419 this.status = status; 420 return this; 421 } 422 423 /** 424 * @since 3.6 425 */ 426 public Date getCreatedAt() { 427 return createdAt; 428 } 429 430 /** 431 * @since 3.6 432 */ 433 public Rule setCreatedAt(Date d) { 434 this.createdAt = d; 435 return this; 436 } 437 438 /** 439 * @since 3.6 440 */ 441 public Date getUpdatedAt() { 442 return updatedAt; 443 } 444 445 /** 446 * @since 3.6 447 */ 448 public Rule setUpdatedAt(Date updatedAt) { 449 this.updatedAt = updatedAt; 450 return this; 451 } 452 453 454 /** 455 * @since 3.6 456 */ 457 public String getLanguage() { 458 return language; 459 } 460 461 /** 462 * For internal use only. 463 * @since 3.6 464 */ 465 public Rule setLanguage(String language) { 466 this.language = language; 467 return this; 468 } 469 470 @Override 471 public boolean equals(Object obj) { 472 if (!(obj instanceof Rule)) { 473 return false; 474 } 475 if (this == obj) { 476 return true; 477 } 478 Rule other = (Rule) obj; 479 return new EqualsBuilder() 480 .append(pluginName, other.getRepositoryKey()) 481 .append(key, other.getKey()) 482 .isEquals(); 483 } 484 485 @Override 486 public int hashCode() { 487 return new HashCodeBuilder(17, 37) 488 .append(pluginName) 489 .append(key) 490 .toHashCode(); 491 } 492 493 @Override 494 public String toString() { 495 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077 496 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 497 .append("id", id) 498 .append("name", name) 499 .append("key", key) 500 .append("configKey", configKey) 501 .append("plugin", pluginName) 502 .append("severity", priority) 503 .append("cardinality", cardinality) 504 .append("status", status) 505 .append("language", language) 506 .append("parent", parent) 507 .toString(); 508 } 509 510 @CheckForNull 511 private String removeNewLineCharacters(@Nullable String text) { 512 String removedCRLF = StringUtils.remove(text, "\n"); 513 removedCRLF = StringUtils.remove(removedCRLF, "\r"); 514 removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); 515 removedCRLF = StringUtils.remove(removedCRLF, "\r\n"); 516 return removedCRLF; 517 } 518 519 public static Rule create() { 520 return new Rule(); 521 } 522 523 /** 524 * Create with all required fields 525 */ 526 public static Rule create(String repositoryKey, String key, String name) { 527 return new Rule().setUniqueKey(repositoryKey, key).setName(name); 528 } 529 530 /** 531 * Create with all required fields 532 * 533 * @since 2.10 534 */ 535 public static Rule create(String repositoryKey, String key) { 536 return new Rule().setUniqueKey(repositoryKey, key); 537 } 538 539 /** 540 * @since 3.6 541 */ 542 public RuleKey ruleKey() { 543 return RuleKey.of(getRepositoryKey(), getKey()); 544 } 545 }