001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program 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 * This program 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 */ 020package org.sonar.api.rules; 021 022import com.google.common.base.Joiner; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.LinkedHashSet; 026import java.util.List; 027import java.util.Set; 028import javax.annotation.CheckForNull; 029import javax.annotation.Nullable; 030import org.apache.commons.lang.StringUtils; 031import org.apache.commons.lang.builder.EqualsBuilder; 032import org.apache.commons.lang.builder.HashCodeBuilder; 033import org.apache.commons.lang.builder.ToStringBuilder; 034import org.apache.commons.lang.builder.ToStringStyle; 035import org.sonar.api.rule.RuleKey; 036import org.sonar.api.utils.SonarException; 037import org.sonar.check.Cardinality; 038 039import static java.util.Arrays.asList; 040import static java.util.Collections.unmodifiableSet; 041 042public class Rule { 043 044 /** 045 * @since 3.6 046 */ 047 public static final String STATUS_BETA = "BETA"; 048 /** 049 * @since 3.6 050 */ 051 public static final String STATUS_DEPRECATED = "DEPRECATED"; 052 /** 053 * @since 3.6 054 */ 055 public static final String STATUS_READY = "READY"; 056 057 /** 058 * For internal use only. 059 * 060 * @since 3.6 061 */ 062 public static final String STATUS_REMOVED = "REMOVED"; 063 064 /** 065 * List of available status 066 * 067 * @since 3.6 068 */ 069 private static final Set<String> STATUS_LIST = unmodifiableSet(new LinkedHashSet<>(asList(STATUS_READY, STATUS_BETA, STATUS_DEPRECATED, STATUS_REMOVED))); 070 071 private Integer id; 072 073 /** 074 * The default priority given to a rule if not explicitly set 075 */ 076 public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR; 077 078 private String name; 079 private String key; 080 private String configKey; 081 private RulePriority priority = DEFAULT_PRIORITY; 082 private String description; 083 private String pluginName; 084 private boolean isTemplate = false; 085 private String status = STATUS_READY; 086 private String language; 087 private Rule template = null; 088 private List<RuleParam> params = new ArrayList<>(); 089 private Date createdAt; 090 private Date updatedAt; 091 private String tags; 092 private String systemTags; 093 094 /** 095 * @deprecated since 2.3. Use the factory method {@link #create()} 096 */ 097 @Deprecated 098 public Rule() { 099 } 100 101 /** 102 * Creates rule with minimum set of info 103 * 104 * @param pluginName the plugin name indicates which plugin the rule belongs to 105 * @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 106 * application 107 * @deprecated since 2.3. Use the factory method {@link #create()} 108 */ 109 @Deprecated 110 public Rule(String pluginName, String key) { 111 this.pluginName = pluginName; 112 this.key = key; 113 this.configKey = key; 114 } 115 116 public Integer getId() { 117 return id; 118 } 119 120 /** 121 * @deprecated since 2.3. visibility should be decreased to protected or package 122 */ 123 @Deprecated 124 public void setId(Integer id) { 125 this.id = id; 126 } 127 128 public String getName() { 129 return name; 130 } 131 132 /** 133 * Sets the rule name 134 */ 135 public Rule setName(String name) { 136 this.name = removeNewLineCharacters(name); 137 return this; 138 } 139 140 public String getKey() { 141 return key; 142 } 143 144 /** 145 * Sets the rule key 146 */ 147 public Rule setKey(String key) { 148 this.key = key; 149 return this; 150 } 151 152 public String getConfigKey() { 153 return configKey; 154 } 155 156 /** 157 * Sets the configuration key 158 */ 159 public Rule setConfigKey(String configKey) { 160 this.configKey = configKey; 161 return this; 162 } 163 164 public String getDescription() { 165 return description; 166 } 167 168 /** 169 * Sets the rule description 170 */ 171 public Rule setDescription(String description) { 172 this.description = StringUtils.strip(description); 173 return this; 174 } 175 176 public Boolean isEnabled() { 177 return !STATUS_REMOVED.equals(status); 178 } 179 180 public List<RuleParam> getParams() { 181 return params; 182 } 183 184 public RuleParam getParam(String key) { 185 for (RuleParam param : params) { 186 if (StringUtils.equals(key, param.getKey())) { 187 return param; 188 } 189 } 190 return null; 191 } 192 193 /** 194 * Sets the rule parameters 195 */ 196 public Rule setParams(List<RuleParam> params) { 197 this.params.clear(); 198 for (RuleParam param : params) { 199 param.setRule(this); 200 this.params.add(param); 201 } 202 return this; 203 } 204 205 public RuleParam createParameter() { 206 RuleParam parameter = new RuleParam() 207 .setRule(this); 208 params.add(parameter); 209 return parameter; 210 } 211 212 public RuleParam createParameter(String key) { 213 RuleParam parameter = new RuleParam() 214 .setKey(key) 215 .setRule(this); 216 params.add(parameter); 217 return parameter; 218 } 219 220 /** 221 * @since 2.5 222 */ 223 public RulePriority getSeverity() { 224 return priority; 225 } 226 227 /** 228 * @param severity severity to set, if null, uses the default priority. 229 * @since 2.5 230 */ 231 public Rule setSeverity(RulePriority severity) { 232 if (severity == null) { 233 this.priority = DEFAULT_PRIORITY; 234 } else { 235 this.priority = severity; 236 } 237 return this; 238 } 239 240 public String getRepositoryKey() { 241 return pluginName; 242 } 243 244 public Rule setRepositoryKey(String s) { 245 this.pluginName = s; 246 return this; 247 } 248 249 public Rule setUniqueKey(String repositoryKey, String key) { 250 return setRepositoryKey(repositoryKey).setKey(key).setConfigKey(key); 251 } 252 253 /** 254 * @since 4.4 255 */ 256 public boolean isTemplate() { 257 return isTemplate; 258 } 259 260 /** 261 * @since 4.4 262 */ 263 public Rule setIsTemplate(boolean isTemplate) { 264 this.isTemplate = isTemplate; 265 return this; 266 } 267 268 /** 269 * @deprecated since 4.4, use {@link #isTemplate()} 270 */ 271 @Deprecated 272 public Cardinality getCardinality() { 273 return isTemplate ? Cardinality.MULTIPLE : Cardinality.SINGLE; 274 } 275 276 /** 277 * @deprecated since 4.4, use {@link #setIsTemplate(boolean)} 278 */ 279 @Deprecated 280 public Rule setCardinality(Cardinality c) { 281 this.isTemplate = Cardinality.MULTIPLE.equals(c); 282 return this; 283 } 284 285 /** 286 * @deprecated since 4.4, use {@link #getTemplate()} 287 */ 288 @Deprecated 289 public Rule getParent() { 290 return getTemplate(); 291 } 292 293 /** 294 * @deprecated since 4.4, use {@link #setTemplate(Rule)}} 295 */ 296 @Deprecated 297 public Rule setParent(Rule parent) { 298 return setTemplate(parent); 299 } 300 301 /** 302 * @since 4.4 303 */ 304 public Rule getTemplate() { 305 return template; 306 } 307 308 /** 309 * @since 4.4 310 */ 311 public Rule setTemplate(Rule template) { 312 this.template = template; 313 return this; 314 } 315 316 /** 317 * @since 3.6 318 */ 319 public String getStatus() { 320 return status; 321 } 322 323 /** 324 * @since 3.6 325 */ 326 public Rule setStatus(String status) { 327 if (!STATUS_LIST.contains(status)) { 328 throw new SonarException("The status of a rule can only contain : " + Joiner.on(", ").join(STATUS_LIST)); 329 } 330 this.status = status; 331 return this; 332 } 333 334 /** 335 * @since 3.6 336 */ 337 public Date getCreatedAt() { 338 return createdAt; 339 } 340 341 /** 342 * @since 3.6 343 */ 344 public Rule setCreatedAt(Date d) { 345 this.createdAt = d; 346 return this; 347 } 348 349 /** 350 * @since 3.6 351 */ 352 public Date getUpdatedAt() { 353 return updatedAt; 354 } 355 356 /** 357 * @since 3.6 358 */ 359 public Rule setUpdatedAt(Date updatedAt) { 360 this.updatedAt = updatedAt; 361 return this; 362 } 363 364 /** 365 * @since 3.6 366 */ 367 public String getLanguage() { 368 return language; 369 } 370 371 /** 372 * For internal use only. 373 * 374 * @since 3.6 375 */ 376 public Rule setLanguage(String language) { 377 this.language = language; 378 return this; 379 } 380 381 /** 382 * For definition of rule only 383 */ 384 public String[] getTags() { 385 return tags == null ? new String[0] : StringUtils.split(tags, ','); 386 } 387 388 /** 389 * For definition of rule only 390 */ 391 public Rule setTags(String[] tags) { 392 this.tags = tags == null ? null : StringUtils.join(tags, ','); 393 return this; 394 } 395 396 /** 397 * For internal use 398 */ 399 public String[] getSystemTags() { 400 return systemTags == null ? new String[0] : StringUtils.split(systemTags, ','); 401 } 402 403 public Rule setSystemTags(String[] tags) { 404 this.systemTags = tags == null ? null : StringUtils.join(tags, ','); 405 return this; 406 } 407 408 /** 409 * For internal use only. 410 * 411 * @deprecated since 4.4, use {@link #getCharacteristicKey()} 412 * @since 4.3 413 */ 414 @CheckForNull 415 @Deprecated 416 public Integer getCharacteristicId() { 417 return null; 418 } 419 420 /** 421 * For internal use only. 422 * 423 * @deprecated since 4.4, use {@link #setCharacteristicKey(String)} 424 * @since 4.3 425 */ 426 @Deprecated 427 public Rule setCharacteristicId(@Nullable Integer characteristicId) { 428 return this; 429 } 430 431 /** 432 * For internal use only. 433 * 434 * @deprecated since 4.4, use {@link #getDefaultCharacteristicKey()} 435 * @since 4.3 436 */ 437 @CheckForNull 438 @Deprecated 439 public Integer getDefaultCharacteristicId() { 440 return null; 441 } 442 443 /** 444 * For internal use only. 445 * 446 * @deprecated since 4.4, use {@link #setDefaultCharacteristicKey(String)} 447 * @since 4.3 448 */ 449 @Deprecated 450 public Rule setDefaultCharacteristicId(@Nullable Integer defaultCharacteristicId) { 451 return this; 452 } 453 454 @Override 455 public boolean equals(Object obj) { 456 if (!(obj instanceof Rule)) { 457 return false; 458 } 459 if (this == obj) { 460 return true; 461 } 462 Rule other = (Rule) obj; 463 return new EqualsBuilder() 464 .append(pluginName, other.getRepositoryKey()) 465 .append(key, other.getKey()) 466 .isEquals(); 467 } 468 469 @Override 470 public int hashCode() { 471 return new HashCodeBuilder(17, 37) 472 .append(pluginName) 473 .append(key) 474 .toHashCode(); 475 } 476 477 @Override 478 public String toString() { 479 // Note that ReflectionToStringBuilder will not work here - see SONAR-3077 480 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 481 .append("id", id) 482 .append("name", name) 483 .append("key", key) 484 .append("configKey", configKey) 485 .append("plugin", pluginName) 486 .append("severity", priority) 487 .append("isTemplate", isTemplate()) 488 .append("status", status) 489 .append("language", language) 490 .append("template", template) 491 .toString(); 492 } 493 494 @CheckForNull 495 private static String removeNewLineCharacters(@Nullable String text) { 496 String removedCRLF = StringUtils.remove(text, "\n"); 497 removedCRLF = StringUtils.remove(removedCRLF, "\r"); 498 removedCRLF = StringUtils.remove(removedCRLF, "\n\r"); 499 removedCRLF = StringUtils.remove(removedCRLF, "\r\n"); 500 return removedCRLF; 501 } 502 503 public static Rule create() { 504 return new Rule(); 505 } 506 507 /** 508 * Create with all required fields 509 */ 510 public static Rule create(String repositoryKey, String key, String name) { 511 return new Rule().setUniqueKey(repositoryKey, key).setName(name); 512 } 513 514 /** 515 * Create with all required fields 516 * 517 * @since 2.10 518 */ 519 public static Rule create(String repositoryKey, String key) { 520 return new Rule().setUniqueKey(repositoryKey, key); 521 } 522 523 /** 524 * @since 3.6 525 */ 526 public RuleKey ruleKey() { 527 return RuleKey.of(getRepositoryKey(), getKey()); 528 } 529 530 /** 531 * @since 4.4 532 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 533 */ 534 @CheckForNull 535 @Deprecated 536 public String getDefaultCharacteristicKey() { 537 return null; 538 } 539 540 /** 541 * @since 4.4 542 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 543 */ 544 @Deprecated 545 public Rule setDefaultCharacteristicKey(@Nullable String defaultCharacteristicKey) { 546 return this; 547 } 548 549 /** 550 * @since 4.4 551 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 552 */ 553 @CheckForNull 554 @Deprecated 555 public String getDefaultSubCharacteristicKey() { 556 return null; 557 } 558 559 /** 560 * @since 4.4 561 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 562 */ 563 @Deprecated 564 public Rule setDefaultSubCharacteristicKey(@Nullable String defaultSubCharacteristicKey) { 565 return this; 566 } 567 568 /** 569 * @since 4.4 570 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 571 */ 572 @CheckForNull 573 @Deprecated 574 public String getCharacteristicKey() { 575 return null; 576 } 577 578 /** 579 * @since 4.4 580 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 581 */ 582 @Deprecated 583 public Rule setCharacteristicKey(@Nullable String characteristicKey) { 584 return this; 585 } 586 587 /** 588 * @since 4.4 589 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 590 */ 591 @CheckForNull 592 @Deprecated 593 public String getSubCharacteristicKey() { 594 return null; 595 } 596 597 /** 598 * @since 4.4 599 * @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. 600 */ 601 @Deprecated 602 public Rule setSubCharacteristicKey(@Nullable String subCharacteristicKey) { 603 return this; 604 } 605}