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 .append("parent", parent) 499 .toString(); 500 } 501 502 private String removeNewLineCharacters(String text) { 503 String removedCRLF = StringUtils.remove(text, "\n"); 504 removedCRLF = StringUtils.remove(removedCRLF, "\r"); 505 removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); 506 removedCRLF = StringUtils.remove(removedCRLF, "\r\n"); 507 return removedCRLF; 508 } 509 510 public static Rule create() { 511 return new Rule(); 512 } 513 514 /** 515 * Create with all required fields 516 */ 517 public static Rule create(String repositoryKey, String key, String name) { 518 return new Rule().setUniqueKey(repositoryKey, key).setName(name); 519 } 520 521 /** 522 * Create with all required fields 523 * 524 * @since 2.10 525 */ 526 public static Rule create(String repositoryKey, String key) { 527 return new Rule().setUniqueKey(repositoryKey, key); 528 } 529 530 /** 531 * @since 3.6 532 */ 533 public static Set<String> getStatusList() { 534 return ImmutableSet.of(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED); 535 } 536 537 /** 538 * @since 3.6 539 */ 540 public RuleKey ruleKey() { 541 return RuleKey.of(getRepositoryKey(), getKey()); 542 } 543}