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