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