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