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