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 021package org.sonar.api.rules; 022 023import com.google.common.base.Joiner; 024import com.google.common.collect.ImmutableSet; 025import org.apache.commons.lang.StringUtils; 026import org.apache.commons.lang.builder.EqualsBuilder; 027import org.apache.commons.lang.builder.HashCodeBuilder; 028import org.apache.commons.lang.builder.ToStringBuilder; 029import org.apache.commons.lang.builder.ToStringStyle; 030import org.sonar.api.database.DatabaseProperties; 031import org.sonar.api.rule.RuleKey; 032import org.sonar.api.utils.SonarException; 033import org.sonar.check.Cardinality; 034 035import javax.annotation.CheckForNull; 036import javax.annotation.Nullable; 037import javax.persistence.Column; 038import javax.persistence.Entity; 039import javax.persistence.EnumType; 040import javax.persistence.Enumerated; 041import javax.persistence.FetchType; 042import javax.persistence.GeneratedValue; 043import javax.persistence.Id; 044import javax.persistence.JoinColumn; 045import javax.persistence.ManyToOne; 046import javax.persistence.OneToMany; 047import javax.persistence.Table; 048import javax.persistence.Temporal; 049import javax.persistence.TemporalType; 050import javax.persistence.Transient; 051 052import java.util.ArrayList; 053import java.util.Date; 054import java.util.List; 055import java.util.Set; 056 057@Entity 058@Table(name = "rules") 059public 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 /** 224 * @deprecated since 2.5 use {@link #getRepositoryKey()} instead 225 */ 226 @Deprecated 227 public String getPluginName() { 228 return pluginName; 229 } 230 231 /** 232 * @deprecated since 2.5 use {@link #setRepositoryKey(String)} instead 233 */ 234 @Deprecated 235 public Rule setPluginName(String pluginName) { 236 this.pluginName = pluginName; 237 return this; 238 } 239 240 public String getConfigKey() { 241 return configKey; 242 } 243 244 /** 245 * Sets the configuration key 246 */ 247 public Rule setConfigKey(String configKey) { 248 this.configKey = configKey; 249 return this; 250 } 251 252 public String getDescription() { 253 return description; 254 } 255 256 /** 257 * Sets the rule description 258 */ 259 public Rule setDescription(String description) { 260 this.description = StringUtils.strip(description); 261 return this; 262 } 263 264 /** 265 * @deprecated in 3.6. Replaced by {@link #setStatus(String status)}. 266 */ 267 @Deprecated 268 public Rule setEnabled(Boolean enabled) { 269 throw new UnsupportedOperationException("No more supported since version 3.6."); 270 } 271 272 public Boolean isEnabled() { 273 return !STATUS_REMOVED.equals(status); 274 } 275 276 public List<RuleParam> getParams() { 277 return params; 278 } 279 280 public RuleParam getParam(String key) { 281 for (RuleParam param : params) { 282 if (StringUtils.equals(key, param.getKey())) { 283 return param; 284 } 285 } 286 return null; 287 } 288 289 /** 290 * Sets the rule parameters 291 */ 292 public Rule setParams(List<RuleParam> params) { 293 this.params.clear(); 294 for (RuleParam param : params) { 295 param.setRule(this); 296 this.params.add(param); 297 } 298 return this; 299 } 300 301 public RuleParam createParameter() { 302 RuleParam parameter = new RuleParam() 303 .setRule(this); 304 params.add(parameter); 305 return parameter; 306 } 307 308 public RuleParam createParameter(String key) { 309 RuleParam parameter = new RuleParam() 310 .setKey(key) 311 .setRule(this); 312 params.add(parameter); 313 return parameter; 314 } 315 316 /** 317 * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2007 318 */ 319 @Deprecated 320 public Integer getCategoryId() { 321 return null; 322 } 323 324 /** 325 * @since 2.5 326 */ 327 public RulePriority getSeverity() { 328 return priority; 329 } 330 331 /** 332 * @param severity severity to set, if null, uses the default priority. 333 * @since 2.5 334 */ 335 public Rule setSeverity(RulePriority severity) { 336 if (severity == null) { 337 this.priority = DEFAULT_PRIORITY; 338 } else { 339 this.priority = severity; 340 } 341 return this; 342 } 343 344 /** 345 * @deprecated since 2.5 use {@link #getSeverity()} instead. See http://jira.codehaus.org/browse/SONAR-1829 346 */ 347 @Deprecated 348 public RulePriority getPriority() { 349 return priority; 350 } 351 352 /** 353 * Sets the rule priority. If null, uses the default priority 354 * 355 * @deprecated since 2.5 use {@link #setSeverity(RulePriority)} instead. See http://jira.codehaus.org/browse/SONAR-1829 356 */ 357 @Deprecated 358 public Rule setPriority(RulePriority priority) { 359 return setSeverity(priority); 360 } 361 362 public String getRepositoryKey() { 363 return pluginName; 364 } 365 366 public Rule setRepositoryKey(String s) { 367 this.pluginName = s; 368 return this; 369 } 370 371 public Rule setUniqueKey(String repositoryKey, String key) { 372 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key); 373 } 374 375 /** 376 * @since 4.4 377 */ 378 public boolean isTemplate() { 379 return isTemplate; 380 } 381 382 /** 383 * @since 4.4 384 */ 385 public Rule setIsTemplate(boolean isTemplate) { 386 this.isTemplate = isTemplate; 387 return this; 388 } 389 390 /** 391 * @deprecated since 4.4, use {@link #isTemplate()} 392 */ 393 @Deprecated 394 public Cardinality getCardinality() { 395 return isTemplate ? Cardinality.MULTIPLE : Cardinality.SINGLE; 396 } 397 398 /** 399 * @deprecated since 4.4, use {@link #setIsTemplate(boolean)} 400 */ 401 @Deprecated 402 public Rule setCardinality(Cardinality c) { 403 this.isTemplate = Cardinality.MULTIPLE.equals(c); 404 return this; 405 } 406 407 /** 408 * @deprecated since 4.4, use {@link #getTemplate()} 409 */ 410 @Deprecated 411 public Rule getParent() { 412 return template; 413 } 414 415 /** 416 * @deprecated since 4.4, use {@link #setTemplate(Rule)}} 417 */ 418 @Deprecated 419 public Rule setParent(Rule parent) { 420 this.template = parent; 421 return this; 422 } 423 424 /** 425 * @since 4.4 426 */ 427 public Rule getTemplate() { 428 return template; 429 } 430 431 /** 432 * @since 4.4 433 */ 434 public Rule setTemplate(Rule template) { 435 this.template = template; 436 return this; 437 } 438 439 /** 440 * @since 3.6 441 */ 442 public String getStatus() { 443 return status; 444 } 445 446 /** 447 * @since 3.6 448 */ 449 public Rule setStatus(String status) { 450 if (!STATUS_LIST.contains(status)) { 451 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST)); 452 } 453 this.status = status; 454 return this; 455 } 456 457 /** 458 * @since 3.6 459 */ 460 public Date getCreatedAt() { 461 return createdAt; 462 } 463 464 /** 465 * @since 3.6 466 */ 467 public Rule setCreatedAt(Date d) { 468 this.createdAt = d; 469 return this; 470 } 471 472 /** 473 * @since 3.6 474 */ 475 public Date getUpdatedAt() { 476 return updatedAt; 477 } 478 479 /** 480 * @since 3.6 481 */ 482 public Rule setUpdatedAt(Date updatedAt) { 483 this.updatedAt = updatedAt; 484 return this; 485 } 486 487 /** 488 * @since 3.6 489 */ 490 public String getLanguage() { 491 return language; 492 } 493 494 /** 495 * For internal use only. 496 * 497 * @since 3.6 498 */ 499 public Rule setLanguage(String language) { 500 this.language = language; 501 return this; 502 } 503 504 /** 505 * For definition of rule only 506 */ 507 public String[] getTags() { 508 return tags; 509 } 510 511 /** 512 * For definition of rule only 513 */ 514 public Rule setTags(String[] tags) { 515 this.tags = tags; 516 return this; 517 } 518 519 /** 520 * For internal use only. 521 * 522 * @deprecated since 4.4, use {@link #getCharacteristicKey()} 523 * @since 4.3 524 */ 525 @CheckForNull 526 @Deprecated 527 public Integer getCharacteristicId() { 528 return characteristicId; 529 } 530 531 /** 532 * For internal use only. 533 * 534 * @deprecated since 4.4, use {@link #setCharacteristicKey(@Nullable String characteristicKey)} 535 * @since 4.3 536 */ 537 @Deprecated 538 public Rule setCharacteristicId(@Nullable Integer characteristicId) { 539 this.characteristicId = characteristicId; 540 return this; 541 } 542 543 /** 544 * For internal use only. 545 * 546 * @deprecated since 4.4, use {@link #getDefaultCharacteristicKey()} 547 * @since 4.3 548 */ 549 @CheckForNull 550 @Deprecated 551 public Integer getDefaultCharacteristicId() { 552 return defaultCharacteristicId; 553 } 554 555 /** 556 * For internal use only. 557 * 558 * @deprecated since 4.4, use {@link #setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey)} 559 * @since 4.3 560 */ 561 @Deprecated 562 public Rule setDefaultCharacteristicId(@Nullable Integer defaultCharacteristicId) { 563 this.defaultCharacteristicId = defaultCharacteristicId; 564 return this; 565 } 566 567 @Override 568 public boolean equals(Object obj) { 569 if (!(obj instanceof Rule)) { 570 return false; 571 } 572 if (this == obj) { 573 return true; 574 } 575 Rule other = (Rule) obj; 576 return new EqualsBuilder() 577 .append(pluginName, other.getRepositoryKey()) 578 .append(key, other.getKey()) 579 .isEquals(); 580 } 581 582 @Override 583 public int hashCode() { 584 return new HashCodeBuilder(17, 37) 585 .append(pluginName) 586 .append(key) 587 .toHashCode(); 588 } 589 590 @Override 591 public String toString() { 592 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077 593 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 594 .append("id", id) 595 .append("name", name) 596 .append("key", key) 597 .append("configKey", configKey) 598 .append("plugin", pluginName) 599 .append("severity", priority) 600 .append("isTemplate", isTemplate()) 601 .append("status", status) 602 .append("language", language) 603 .append("template", template) 604 .toString(); 605 } 606 607 @CheckForNull 608 private String removeNewLineCharacters(@Nullable String text) { 609 String removedCRLF = StringUtils.remove(text, "\n"); 610 removedCRLF = StringUtils.remove(removedCRLF, "\r"); 611 removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); 612 removedCRLF = StringUtils.remove(removedCRLF, "\r\n"); 613 return removedCRLF; 614 } 615 616 public static Rule create() { 617 return new Rule(); 618 } 619 620 /** 621 * Create with all required fields 622 */ 623 public static Rule create(String repositoryKey, String key, String name) { 624 return new Rule().setUniqueKey(repositoryKey, key).setName(name); 625 } 626 627 /** 628 * Create with all required fields 629 * 630 * @since 2.10 631 */ 632 public static Rule create(String repositoryKey, String key) { 633 return new Rule().setUniqueKey(repositoryKey, key); 634 } 635 636 /** 637 * @since 3.6 638 */ 639 public RuleKey ruleKey() { 640 return RuleKey.of(getRepositoryKey(), getKey()); 641 } 642 643 /** 644 * @since 4.4 645 */ 646 @CheckForNull 647 public String getDefaultCharacteristicKey() { 648 return defaultCharacteristicKey; 649 } 650 651 /** 652 * @since 4.4 653 */ 654 public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) { 655 this.defaultCharacteristicKey = defaultCharacteristicKey; 656 return this; 657 } 658 659 /** 660 * @since 4.4 661 */ 662 @CheckForNull 663 public String getDefaultSubCharacteristicKey() { 664 return defaultSubCharacteristicKey; 665 } 666 667 /** 668 * @since 4.4 669 */ 670 public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) { 671 this.defaultSubCharacteristicKey = defaultSubCharacteristicKey; 672 return this; 673 } 674 675 /** 676 * @since 4.4 677 */ 678 @CheckForNull 679 public String getCharacteristicKey() { 680 return characteristicKey; 681 } 682 683 /** 684 * @since 4.4 685 */ 686 public Rule setCharacteristicKey(@Nullable String characteristicKey) { 687 this.characteristicKey = characteristicKey; 688 return this; 689 } 690 691 /** 692 * @since 4.4 693 */ 694 @CheckForNull 695 public String getSubCharacteristicKey() { 696 return subCharacteristicKey; 697 } 698 699 /** 700 * @since 4.4 701 */ 702 public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) { 703 this.subCharacteristicKey = subCharacteristicKey; 704 return this; 705 } 706}