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