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