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 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.Column; 038 import javax.persistence.Entity; 039 import javax.persistence.EnumType; 040 import javax.persistence.Enumerated; 041 import javax.persistence.FetchType; 042 import javax.persistence.GeneratedValue; 043 import javax.persistence.Id; 044 import javax.persistence.JoinColumn; 045 import javax.persistence.ManyToOne; 046 import javax.persistence.OneToMany; 047 import javax.persistence.Table; 048 import javax.persistence.Temporal; 049 import javax.persistence.TemporalType; 050 import javax.persistence.Transient; 051 052 import java.util.ArrayList; 053 import java.util.Date; 054 import java.util.List; 055 import java.util.Set; 056 057 @Entity 058 @Table(name = "rules") 059 public class Rule { 060 061 /** 062 * @since 3.6 063 */ 064 public static final String STATUS_BETA = "BETA"; 065 /** 066 * @since 3.6 067 */ 068 public static final String STATUS_DEPRECATED = "DEPRECATED"; 069 /** 070 * @since 3.6 071 */ 072 public static final String STATUS_READY = "READY"; 073 074 /** 075 * For internal use only. 076 * 077 * @since 3.6 078 */ 079 public static final String STATUS_REMOVED = "REMOVED"; 080 081 /** 082 * List of available status 083 * 084 * @since 3.6 085 */ 086 private static final Set<String> STATUS_LIST = ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED); 087 088 /** 089 * @since 4.2 090 */ 091 private static final String[] DEFAULT_TAGS = new String[0]; 092 093 @Id 094 @Column(name = "id") 095 @GeneratedValue 096 private Integer id; 097 098 /** 099 * The default priority given to a rule if not explicitly set 100 */ 101 public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR; 102 103 @Column(name = "name", updatable = true, nullable = true, length = 200) 104 private String name; 105 106 @Column(name = "plugin_rule_key", updatable = false, nullable = true, length = 200) 107 private String key; 108 109 @Column(name = "plugin_config_key", updatable = true, nullable = true, length = 500) 110 private String configKey; 111 112 @Column(name = "priority", updatable = true, nullable = true) 113 @Enumerated(EnumType.ORDINAL) 114 private RulePriority priority = DEFAULT_PRIORITY; 115 116 @Column(name = "description", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE) 117 private String description; 118 119 @Column(name = "plugin_name", updatable = true, nullable = false) 120 private String pluginName; 121 122 @Enumerated(EnumType.STRING) 123 @Column(name = "is_template", updatable = true, nullable = false) 124 private boolean isTemplate = false; 125 126 @Column(name = "status", updatable = true, nullable = true) 127 private String status = STATUS_READY; 128 129 @Column(name = "language", updatable = true, nullable = true) 130 private String language; 131 132 @ManyToOne(fetch = FetchType.EAGER) 133 @JoinColumn(name = "template_id", updatable = true, nullable = true) 134 private Rule template = null; 135 136 @Column(name = "characteristic_id", updatable = true, nullable = true) 137 private Integer characteristicId; 138 139 @Column(name = "default_characteristic_id", updatable = true, nullable = true) 140 private Integer defaultCharacteristicId; 141 142 @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) 143 @OneToMany(mappedBy = "rule") 144 private List<RuleParam> params = new ArrayList<RuleParam>(); 145 146 @Temporal(TemporalType.TIMESTAMP) 147 @Column(name = "created_at", updatable = true, nullable = true) 148 private Date createdAt; 149 150 @Temporal(TemporalType.TIMESTAMP) 151 @Column(name = "updated_at", updatable = true, nullable = true) 152 private Date updatedAt; 153 154 @Transient 155 private String defaultCharacteristicKey; 156 @Transient 157 private String defaultSubCharacteristicKey; 158 @Transient 159 private String characteristicKey; 160 @Transient 161 private String subCharacteristicKey; 162 163 private transient String[] tags = DEFAULT_TAGS; 164 165 /** 166 * @deprecated since 2.3. Use the factory method {@link #create()} 167 */ 168 @Deprecated 169 public Rule() { 170 } 171 172 /** 173 * Creates rule with minimum set of info 174 * 175 * @param pluginName the plugin name indicates which plugin the rule belongs to 176 * @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 177 * application 178 * @deprecated since 2.3. Use the factory method {@link #create()} 179 */ 180 @Deprecated 181 public Rule(String pluginName, String key) { 182 this.pluginName = pluginName; 183 this.key = key; 184 this.configKey = key; 185 } 186 187 public Integer getId() { 188 return id; 189 } 190 191 /** 192 * @deprecated since 2.3. visibility should be decreased to protected or package 193 */ 194 @Deprecated 195 public void setId(Integer id) { 196 this.id = id; 197 } 198 199 public String getName() { 200 return name; 201 } 202 203 /** 204 * Sets the rule name 205 */ 206 public Rule setName(String name) { 207 this.name = removeNewLineCharacters(name); 208 return this; 209 } 210 211 public String getKey() { 212 return key; 213 } 214 215 /** 216 * Sets the rule key 217 */ 218 public Rule setKey(String key) { 219 this.key = key; 220 return this; 221 } 222 223 public String getConfigKey() { 224 return configKey; 225 } 226 227 /** 228 * Sets the configuration key 229 */ 230 public Rule setConfigKey(String configKey) { 231 this.configKey = configKey; 232 return this; 233 } 234 235 public String getDescription() { 236 return description; 237 } 238 239 /** 240 * Sets the rule description 241 */ 242 public Rule setDescription(String description) { 243 this.description = StringUtils.strip(description); 244 return this; 245 } 246 247 public Boolean isEnabled() { 248 return !STATUS_REMOVED.equals(status); 249 } 250 251 public List<RuleParam> getParams() { 252 return params; 253 } 254 255 public RuleParam getParam(String key) { 256 for (RuleParam param : params) { 257 if (StringUtils.equals(key, param.getKey())) { 258 return param; 259 } 260 } 261 return null; 262 } 263 264 /** 265 * Sets the rule parameters 266 */ 267 public Rule setParams(List<RuleParam> params) { 268 this.params.clear(); 269 for (RuleParam param : params) { 270 param.setRule(this); 271 this.params.add(param); 272 } 273 return this; 274 } 275 276 public RuleParam createParameter() { 277 RuleParam parameter = new RuleParam() 278 .setRule(this); 279 params.add(parameter); 280 return parameter; 281 } 282 283 public RuleParam createParameter(String key) { 284 RuleParam parameter = new RuleParam() 285 .setKey(key) 286 .setRule(this); 287 params.add(parameter); 288 return parameter; 289 } 290 291 /** 292 * @since 2.5 293 */ 294 public RulePriority getSeverity() { 295 return priority; 296 } 297 298 /** 299 * @param severity severity to set, if null, uses the default priority. 300 * @since 2.5 301 */ 302 public Rule setSeverity(RulePriority severity) { 303 if (severity == null) { 304 this.priority = DEFAULT_PRIORITY; 305 } else { 306 this.priority = severity; 307 } 308 return this; 309 } 310 311 public String getRepositoryKey() { 312 return pluginName; 313 } 314 315 public Rule setRepositoryKey(String s) { 316 this.pluginName = s; 317 return this; 318 } 319 320 public Rule setUniqueKey(String repositoryKey, String key) { 321 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key); 322 } 323 324 /** 325 * @since 4.4 326 */ 327 public boolean isTemplate() { 328 return isTemplate; 329 } 330 331 /** 332 * @since 4.4 333 */ 334 public Rule setIsTemplate(boolean isTemplate) { 335 this.isTemplate = isTemplate; 336 return this; 337 } 338 339 /** 340 * @deprecated since 4.4, use {@link #isTemplate()} 341 */ 342 @Deprecated 343 public Cardinality getCardinality() { 344 return isTemplate ? Cardinality.MULTIPLE : Cardinality.SINGLE; 345 } 346 347 /** 348 * @deprecated since 4.4, use {@link #setIsTemplate(boolean)} 349 */ 350 @Deprecated 351 public Rule setCardinality(Cardinality c) { 352 this.isTemplate = Cardinality.MULTIPLE.equals(c); 353 return this; 354 } 355 356 /** 357 * @deprecated since 4.4, use {@link #getTemplate()} 358 */ 359 @Deprecated 360 public Rule getParent() { 361 return template; 362 } 363 364 /** 365 * @deprecated since 4.4, use {@link #setTemplate(Rule)}} 366 */ 367 @Deprecated 368 public Rule setParent(Rule parent) { 369 this.template = parent; 370 return this; 371 } 372 373 /** 374 * @since 4.4 375 */ 376 public Rule getTemplate() { 377 return template; 378 } 379 380 /** 381 * @since 4.4 382 */ 383 public Rule setTemplate(Rule template) { 384 this.template = template; 385 return this; 386 } 387 388 /** 389 * @since 3.6 390 */ 391 public String getStatus() { 392 return status; 393 } 394 395 /** 396 * @since 3.6 397 */ 398 public Rule setStatus(String status) { 399 if (!STATUS_LIST.contains(status)) { 400 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST)); 401 } 402 this.status = status; 403 return this; 404 } 405 406 /** 407 * @since 3.6 408 */ 409 public Date getCreatedAt() { 410 return createdAt; 411 } 412 413 /** 414 * @since 3.6 415 */ 416 public Rule setCreatedAt(Date d) { 417 this.createdAt = d; 418 return this; 419 } 420 421 /** 422 * @since 3.6 423 */ 424 public Date getUpdatedAt() { 425 return updatedAt; 426 } 427 428 /** 429 * @since 3.6 430 */ 431 public Rule setUpdatedAt(Date updatedAt) { 432 this.updatedAt = updatedAt; 433 return this; 434 } 435 436 /** 437 * @since 3.6 438 */ 439 public String getLanguage() { 440 return language; 441 } 442 443 /** 444 * For internal use only. 445 * 446 * @since 3.6 447 */ 448 public Rule setLanguage(String language) { 449 this.language = language; 450 return this; 451 } 452 453 /** 454 * For definition of rule only 455 */ 456 public String[] getTags() { 457 return tags; 458 } 459 460 /** 461 * For definition of rule only 462 */ 463 public Rule setTags(String[] tags) { 464 this.tags = tags; 465 return this; 466 } 467 468 /** 469 * For internal use only. 470 * 471 * @deprecated since 4.4, use {@link #getCharacteristicKey()} 472 * @since 4.3 473 */ 474 @CheckForNull 475 @Deprecated 476 public Integer getCharacteristicId() { 477 return characteristicId; 478 } 479 480 /** 481 * For internal use only. 482 * 483 * @deprecated since 4.4, use {@link #setCharacteristicKey(String)} 484 * @since 4.3 485 */ 486 @Deprecated 487 public Rule setCharacteristicId(@Nullable Integer characteristicId) { 488 this.characteristicId = characteristicId; 489 return this; 490 } 491 492 /** 493 * For internal use only. 494 * 495 * @deprecated since 4.4, use {@link #getDefaultCharacteristicKey()} 496 * @since 4.3 497 */ 498 @CheckForNull 499 @Deprecated 500 public Integer getDefaultCharacteristicId() { 501 return defaultCharacteristicId; 502 } 503 504 /** 505 * For internal use only. 506 * 507 * @deprecated since 4.4, use {@link #setDefaultCharacteristicKey(String)} 508 * @since 4.3 509 */ 510 @Deprecated 511 public Rule setDefaultCharacteristicId(@Nullable Integer defaultCharacteristicId) { 512 this.defaultCharacteristicId = defaultCharacteristicId; 513 return this; 514 } 515 516 @Override 517 public boolean equals(Object obj) { 518 if (!(obj instanceof Rule)) { 519 return false; 520 } 521 if (this == obj) { 522 return true; 523 } 524 Rule other = (Rule) obj; 525 return new EqualsBuilder() 526 .append(pluginName, other.getRepositoryKey()) 527 .append(key, other.getKey()) 528 .isEquals(); 529 } 530 531 @Override 532 public int hashCode() { 533 return new HashCodeBuilder(17, 37) 534 .append(pluginName) 535 .append(key) 536 .toHashCode(); 537 } 538 539 @Override 540 public String toString() { 541 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077 542 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 543 .append("id", id) 544 .append("name", name) 545 .append("key", key) 546 .append("configKey", configKey) 547 .append("plugin", pluginName) 548 .append("severity", priority) 549 .append("isTemplate", isTemplate()) 550 .append("status", status) 551 .append("language", language) 552 .append("template", template) 553 .toString(); 554 } 555 556 @CheckForNull 557 private String removeNewLineCharacters(@Nullable String text) { 558 String removedCRLF = StringUtils.remove(text, "\n"); 559 removedCRLF = StringUtils.remove(removedCRLF, "\r"); 560 removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); 561 removedCRLF = StringUtils.remove(removedCRLF, "\r\n"); 562 return removedCRLF; 563 } 564 565 public static Rule create() { 566 return new Rule(); 567 } 568 569 /** 570 * Create with all required fields 571 */ 572 public static Rule create(String repositoryKey, String key, String name) { 573 return new Rule().setUniqueKey(repositoryKey, key).setName(name); 574 } 575 576 /** 577 * Create with all required fields 578 * 579 * @since 2.10 580 */ 581 public static Rule create(String repositoryKey, String key) { 582 return new Rule().setUniqueKey(repositoryKey, key); 583 } 584 585 /** 586 * @since 3.6 587 */ 588 public RuleKey ruleKey() { 589 return RuleKey.of(getRepositoryKey(), getKey()); 590 } 591 592 /** 593 * @since 4.4 594 */ 595 @CheckForNull 596 public String getDefaultCharacteristicKey() { 597 return defaultCharacteristicKey; 598 } 599 600 /** 601 * @since 4.4 602 */ 603 public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) { 604 this.defaultCharacteristicKey = defaultCharacteristicKey; 605 return this; 606 } 607 608 /** 609 * @since 4.4 610 */ 611 @CheckForNull 612 public String getDefaultSubCharacteristicKey() { 613 return defaultSubCharacteristicKey; 614 } 615 616 /** 617 * @since 4.4 618 */ 619 public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) { 620 this.defaultSubCharacteristicKey = defaultSubCharacteristicKey; 621 return this; 622 } 623 624 /** 625 * @since 4.4 626 */ 627 @CheckForNull 628 public String getCharacteristicKey() { 629 return characteristicKey; 630 } 631 632 /** 633 * @since 4.4 634 */ 635 public Rule setCharacteristicKey(@Nullable String characteristicKey) { 636 this.characteristicKey = characteristicKey; 637 return this; 638 } 639 640 /** 641 * @since 4.4 642 */ 643 @CheckForNull 644 public String getSubCharacteristicKey() { 645 return subCharacteristicKey; 646 } 647 648 /** 649 * @since 4.4 650 */ 651 public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) { 652 this.subCharacteristicKey = subCharacteristicKey; 653 return this; 654 } 655 }