001 /* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 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 package org.sonar.api.measures; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.apache.commons.lang.builder.ReflectionToStringBuilder; 024 import org.apache.commons.lang.builder.ToStringStyle; 025 import org.sonar.api.BatchExtension; 026 import org.sonar.api.ServerExtension; 027 import org.sonar.api.batch.InstantiationStrategy; 028 029 import javax.annotation.CheckForNull; 030 import javax.annotation.Nullable; 031 import javax.persistence.Column; 032 import javax.persistence.Entity; 033 import javax.persistence.EnumType; 034 import javax.persistence.Enumerated; 035 import javax.persistence.GeneratedValue; 036 import javax.persistence.Id; 037 import javax.persistence.Table; 038 import javax.persistence.Transient; 039 040 import java.io.Serializable; 041 042 /** 043 * This class represents the definition of a metric in Sonar. 044 * 045 * @since 1.10 046 */ 047 @Table(name = "metrics") 048 @Entity(name = "Metric") 049 @InstantiationStrategy(InstantiationStrategy.PER_BATCH) 050 public class Metric<G extends Serializable> implements ServerExtension, BatchExtension, Serializable, org.sonar.api.batch.measure.Metric<G> { 051 052 /** 053 * A metric bigger value means a degradation 054 */ 055 public static final int DIRECTION_WORST = -1; 056 /** 057 * A metric bigger value means an improvement 058 */ 059 public static final int DIRECTION_BETTER = 1; 060 /** 061 * The metric direction has no meaning 062 */ 063 public static final int DIRECTION_NONE = 0; 064 065 public enum ValueType { 066 INT(Integer.class), 067 FLOAT(Double.class), 068 PERCENT(Double.class), 069 BOOL(Boolean.class), 070 STRING(String.class), 071 MILLISEC(Integer.class), 072 DATA(String.class), 073 LEVEL(Metric.Level.class), 074 DISTRIB(String.class), 075 RATING(String.class), 076 WORK_DUR(Long.class); 077 078 private final Class valueClass; 079 080 private ValueType(Class valueClass) { 081 this.valueClass = valueClass; 082 } 083 084 private Class valueType() { 085 return valueClass; 086 } 087 088 } 089 090 public enum Level { 091 OK("Green"), WARN("Orange"), ERROR("Red"); 092 093 private String colorName; 094 095 Level(String colorName) { 096 this.colorName = colorName; 097 } 098 099 public String getColorName() { 100 return colorName; 101 } 102 } 103 104 public enum Origin { 105 JAV, GUI, WS 106 } 107 108 @Id 109 @Column(name = "id") 110 @GeneratedValue 111 private Integer id; 112 113 @Transient 114 private transient Formula formula; 115 116 @Column(name = "name", updatable = false, nullable = false, length = 64) 117 private String key; 118 119 @Column(name = "description", updatable = true, nullable = true, length = 255) 120 private String description; 121 122 @Column(name = "val_type", updatable = true, nullable = true) 123 @Enumerated(EnumType.STRING) 124 private ValueType type; 125 126 @Column(name = "direction", updatable = true, nullable = true) 127 private Integer direction; 128 129 @Column(name = "domain", updatable = true, nullable = true, length = 60) 130 private String domain; 131 132 @Column(name = "short_name", updatable = true, nullable = true, length = 64) 133 private String name; 134 135 @Column(name = "qualitative", updatable = true, nullable = true) 136 private Boolean qualitative = Boolean.FALSE; 137 138 @Column(name = "user_managed", updatable = true, nullable = true) 139 private Boolean userManaged = Boolean.FALSE; 140 141 @Column(name = "enabled", updatable = true, nullable = true) 142 private Boolean enabled = Boolean.TRUE; 143 144 @Column(name = "origin", updatable = true, nullable = true, length = 3) 145 @Enumerated(EnumType.STRING) 146 private Origin origin = Origin.JAV; 147 148 @Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20) 149 private Double worstValue; 150 151 @Column(name = "best_value", updatable = true, nullable = true, precision = 30, scale = 20) 152 private Double bestValue; 153 154 @Column(name = "optimized_best_value", updatable = true, nullable = true) 155 private Boolean optimizedBestValue; 156 157 @Column(name = "hidden", updatable = true, nullable = true) 158 private Boolean hidden = Boolean.FALSE; 159 160 @Column(name = "delete_historical_data", updatable = true, nullable = true) 161 private Boolean deleteHistoricalData; 162 163 private Metric(Builder builder) { 164 this.key = builder.key; 165 this.name = builder.name; 166 this.description = builder.description; 167 this.type = builder.type; 168 this.direction = builder.direction; 169 this.domain = builder.domain; 170 this.qualitative = builder.qualitative; 171 this.enabled = Boolean.TRUE; 172 this.worstValue = builder.worstValue; 173 this.optimizedBestValue = builder.optimizedBestValue; 174 this.bestValue = builder.bestValue; 175 this.hidden = builder.hidden; 176 this.formula = builder.formula; 177 this.userManaged = builder.userManaged; 178 this.deleteHistoricalData = builder.deleteHistoricalData; 179 } 180 181 /** 182 * Creates an empty metric. Required for Hibernate. 183 * 184 * @deprecated in 1.12. Use the {@link Builder} factory. 185 */ 186 @Deprecated 187 public Metric() { 188 } 189 190 /** 191 * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT) 192 * 193 * @param key the metric key 194 * @deprecated since 2.7 use the {@link Builder} factory. 195 */ 196 @Deprecated 197 public Metric(String key) { 198 this(key, ValueType.INT); 199 } 200 201 /** 202 * Creates a metric based on a key and a type. Shortcut to 203 * Metric(key, key, key, type, -1, Boolean.FALSE, null, false) 204 * 205 * @param key the key 206 * @param type the type 207 * @deprecated since 2.7 use the {@link Builder} factory. 208 */ 209 @Deprecated 210 public Metric(String key, ValueType type) { 211 this(key, key, key, type, -1, Boolean.FALSE, null, false); 212 } 213 214 /** 215 * @deprecated since 2.7 use the {@link Builder} factory. 216 */ 217 @Deprecated 218 public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) { 219 this(key, name, description, type, direction, qualitative, domain, false); 220 } 221 222 /** 223 * Creates a fully qualified metric. This defaults some values: 224 * <ul> 225 * <li>origin : Origin.JAV</li> 226 * </ul> 227 * 228 * @param key the metric key 229 * @param name the metric name 230 * @param description the metric description 231 * @param type the metric type 232 * @param direction the metric direction 233 * @param qualitative whether the metric is qualitative 234 * @param domain the metric domain 235 * @param userManaged whether the metric is user managed 236 */ 237 private Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain, 238 boolean userManaged) { 239 this.key = key; 240 this.description = description; 241 this.type = type; 242 this.direction = direction; 243 this.domain = domain; 244 this.name = name; 245 this.qualitative = qualitative; 246 this.userManaged = userManaged; 247 this.origin = Origin.JAV; 248 if (ValueType.PERCENT.equals(this.type)) { 249 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); 250 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); 251 } 252 } 253 254 /** 255 * For internal use only 256 */ 257 public Integer getId() { 258 return id; 259 } 260 261 /** 262 * For internal use only 263 */ 264 public Metric setId(@Nullable Integer id) { 265 this.id = id; 266 return this; 267 } 268 269 /** 270 * @return the metric formula 271 */ 272 public Formula getFormula() { 273 return formula; 274 } 275 276 /** 277 * Sets the metric formula 278 * 279 * @param formula the formula 280 * @return this 281 */ 282 public Metric setFormula(Formula formula) { 283 this.formula = formula; 284 return this; 285 } 286 287 /** 288 * @return wether the metric is qualitative 289 */ 290 public Boolean getQualitative() { 291 return qualitative; 292 } 293 294 /** 295 * Sets whether the metric is qualitative 296 * 297 * @param qualitative whether the metric is qualitative 298 * @return this 299 */ 300 public Metric setQualitative(Boolean qualitative) { 301 this.qualitative = qualitative; 302 return this; 303 } 304 305 /** 306 * @return the metric key 307 */ 308 public String getKey() { 309 return key; 310 } 311 312 /** 313 * Sets the metric key 314 * 315 * @param key the key 316 * @return this 317 */ 318 public Metric setKey(String key) { 319 this.key = key; 320 return this; 321 } 322 323 /** 324 * @return the metric type 325 */ 326 public ValueType getType() { 327 return type; 328 } 329 330 /** 331 * Sets the metric type 332 * 333 * @param type the type 334 * @return this 335 */ 336 public Metric setType(ValueType type) { 337 this.type = type; 338 return this; 339 } 340 341 /** 342 * @return the metric description 343 */ 344 @CheckForNull 345 public String getDescription() { 346 return description; 347 } 348 349 /** 350 * Sets the metric description 351 * 352 * @param description the description 353 * @return this 354 */ 355 public Metric setDescription(@Nullable String description) { 356 this.description = description; 357 return this; 358 } 359 360 /** 361 * @return whether the metric is a managed by the users ("manual metric") 362 */ 363 public Boolean getUserManaged() { 364 return userManaged; 365 } 366 367 /** 368 * Sets whether the metric is managed by users ("manual metric") 369 * 370 * @param userManaged whether the metric is user managed 371 * @return this 372 */ 373 public Metric setUserManaged(Boolean userManaged) { 374 this.userManaged = userManaged; 375 return this; 376 } 377 378 /** 379 * @return whether the metric is enabled 380 */ 381 public Boolean getEnabled() { 382 return enabled; 383 } 384 385 /** 386 * Sets whether the metric is enabled 387 * 388 * @param enabled whether the metric is enabled 389 * @return this 390 */ 391 public Metric setEnabled(Boolean enabled) { 392 this.enabled = enabled; 393 return this; 394 } 395 396 /** 397 * @return the metric direction 398 */ 399 public Integer getDirection() { 400 return direction; 401 } 402 403 /** 404 * Sets the metric direction. 405 * 406 * @param direction the direction 407 */ 408 public Metric setDirection(Integer direction) { 409 this.direction = direction; 410 return this; 411 } 412 413 /** 414 * @return the domain of the metric 415 */ 416 public String getDomain() { 417 return domain; 418 } 419 420 /** 421 * Sets the domain for the metric (General, Complexity...) 422 * 423 * @param domain the domain 424 * @return this 425 */ 426 public Metric setDomain(String domain) { 427 this.domain = domain; 428 return this; 429 } 430 431 /** 432 * @return the metric name 433 */ 434 public String getName() { 435 return name; 436 } 437 438 /** 439 * Sets the metric name 440 * 441 * @param name the name 442 * @return this 443 */ 444 public Metric setName(String name) { 445 this.name = name; 446 return this; 447 } 448 449 /** 450 * @return the origin of the metric - Internal use only 451 */ 452 public Origin getOrigin() { 453 return origin; 454 } 455 456 /** 457 * Set the origin of the metric - Internal use only 458 * 459 * @param origin the origin 460 * @return this 461 */ 462 public Metric setOrigin(Origin origin) { 463 this.origin = origin; 464 return this; 465 } 466 467 public Double getWorstValue() { 468 return worstValue; 469 } 470 471 @CheckForNull 472 public Double getBestValue() { 473 return bestValue; 474 } 475 476 /** 477 * @return this 478 */ 479 public Metric setWorstValue(@Nullable Double d) { 480 this.worstValue = d; 481 return this; 482 } 483 484 /** 485 * @param bestValue the best value. It can be null. 486 * @return this 487 */ 488 public Metric setBestValue(@Nullable Double bestValue) { 489 this.bestValue = bestValue; 490 return this; 491 } 492 493 /** 494 * @return whether the metric is of a numeric type (int, percentage...) 495 */ 496 public boolean isNumericType() { 497 return ValueType.INT.equals(type) 498 || ValueType.FLOAT.equals(type) 499 || ValueType.PERCENT.equals(type) 500 || ValueType.BOOL.equals(type) 501 || ValueType.MILLISEC.equals(type) 502 || ValueType.RATING.equals(type) 503 || ValueType.WORK_DUR.equals(type); 504 } 505 506 /** 507 * @return whether the metric is of type data 508 */ 509 public boolean isDataType() { 510 return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type); 511 } 512 513 /** 514 * @return whether the metric is of type percentage 515 */ 516 public boolean isPercentageType() { 517 return ValueType.PERCENT.equals(type); 518 } 519 520 public Metric setOptimizedBestValue(@Nullable Boolean b) { 521 this.optimizedBestValue = b; 522 return this; 523 } 524 525 /** 526 * @return null for manual metrics 527 */ 528 @CheckForNull 529 public Boolean isOptimizedBestValue() { 530 return optimizedBestValue; 531 } 532 533 public Boolean isHidden() { 534 return hidden; 535 } 536 537 public Metric setHidden(Boolean hidden) { 538 this.hidden = hidden; 539 return this; 540 } 541 542 public Boolean getDeleteHistoricalData() { 543 return deleteHistoricalData; 544 } 545 546 @Override 547 public int hashCode() { 548 return key.hashCode(); 549 } 550 551 @Override 552 public boolean equals(Object obj) { 553 if (!(obj instanceof Metric)) { 554 return false; 555 } 556 if (this == obj) { 557 return true; 558 } 559 Metric other = (Metric) obj; 560 return key.equals(other.getKey()); 561 } 562 563 @Override 564 public String toString() { 565 return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); 566 } 567 568 /** 569 * Merge with fields from other metric. All fields are copied, except the id. 570 * 571 * @return this 572 */ 573 public Metric merge(final Metric with) { 574 this.description = with.description; 575 this.domain = with.domain; 576 this.enabled = with.enabled; 577 this.qualitative = with.qualitative; 578 this.worstValue = with.worstValue; 579 this.bestValue = with.bestValue; 580 this.optimizedBestValue = with.optimizedBestValue; 581 this.direction = with.direction; 582 this.key = with.key; 583 this.type = with.type; 584 this.name = with.name; 585 this.userManaged = with.userManaged; 586 this.origin = with.origin; 587 this.hidden = with.hidden; 588 this.deleteHistoricalData = with.deleteHistoricalData; 589 return this; 590 } 591 592 /** 593 * Metric.Builder is used to create metric definitions. It must be preferred to creating new instances of the Metric class directly. 594 * 595 * @since 2.7 596 */ 597 public static final class Builder { 598 private String key; 599 private Metric.ValueType type; 600 private String name; 601 private String description; 602 private Integer direction = DIRECTION_NONE; 603 private Boolean qualitative = Boolean.FALSE; 604 private String domain = null; 605 private Formula formula; 606 private Double worstValue; 607 private Double bestValue; 608 private boolean optimizedBestValue = false; 609 private boolean hidden = false; 610 private boolean userManaged = false; 611 private boolean deleteHistoricalData = false; 612 613 /** 614 * Creates a new {@link Builder} object. 615 * 616 * @param key the metric key, should be unique among all metrics 617 * @param name the metric name 618 * @param type the metric type 619 */ 620 public Builder(String key, String name, ValueType type) { 621 if (StringUtils.isBlank(key)) { 622 throw new IllegalArgumentException("Metric key can not be blank"); 623 } 624 if (StringUtils.isBlank(name)) { 625 throw new IllegalArgumentException("Metric name can not be blank"); 626 } 627 if (type == null) { 628 throw new IllegalArgumentException("Metric type can not be null"); 629 } 630 this.key = key; 631 this.name = name; 632 this.type = type; 633 } 634 635 /** 636 * Sets the metric description. 637 * 638 * @param d the description 639 * @return the builder 640 */ 641 public Builder setDescription(String d) { 642 this.description = d; 643 return this; 644 } 645 646 /** 647 * Sets the metric direction (used for numeric values only), which is used in the Web UI to show if the trend of a metric is good or not. 648 * <ul> 649 * <li>Metric.DIRECTION_WORST: indicates that an increase of the metric value is not a good thing (example: the complexity of a function)</li> 650 * <li>Metric.DIRECTION_BETTER: indicates that an increase of the metric value is a good thing (example: the code coverage of a function)</li> 651 * <li>Metric.DIRECTION_NONE: indicates that the variation of the metric value is neither good nor bad (example: number of files).</li> 652 * </ul> 653 * Metric.DIRECTION_NONE is the default value. 654 * 655 * @see Metric#DIRECTION_WORST 656 * @see Metric#DIRECTION_BETTER 657 * @see Metric#DIRECTION_NONE 658 * 659 * @param d the direction 660 * @return the builder 661 */ 662 public Builder setDirection(Integer d) { 663 this.direction = d; 664 return this; 665 } 666 667 /** 668 * Sets whether the metric is qualitative or not. Default value is false. 669 * <br/> 670 * If set to true, then variations of this metric will be highlighted in the Web UI (for instance, trend icons will be red or green instead of default grey). 671 * 672 * @param b Boolean.TRUE if the metric is qualitative 673 * @return the builder 674 */ 675 public Builder setQualitative(Boolean b) { 676 this.qualitative = b; 677 return this; 678 } 679 680 /** 681 * Sets the domain for the metric (General, Complexity...). This is used to group metrics in the Web UI. 682 * <br/> 683 * By default, the metric belongs to no specific domain. 684 * 685 * @param d the domain 686 * @return the builder 687 */ 688 public Builder setDomain(String d) { 689 this.domain = d; 690 return this; 691 } 692 693 /** 694 * Specifies the formula used by Sonar to automatically aggregate measures stored on files up to the project level. 695 * <br/> 696 * <br/> 697 * By default, no formula is defined, which means that it's up to a sensor/decorator to compute measures on appropriate levels. 698 * <br/> 699 * When a formula is set, sensors/decorators just need to store measures at a specific level and let Sonar run the formula to store 700 * measures on the remaining levels. 701 * 702 * @see SumChildDistributionFormula 703 * @see SumChildValuesFormula 704 * @see MeanAggregationFormula 705 * @see WeightedMeanAggregationFormula 706 * 707 * @param f the formula 708 * @return the builder 709 */ 710 public Builder setFormula(Formula f) { 711 this.formula = f; 712 return this; 713 } 714 715 /** 716 * Sets the worst value that the metric can get (example: 0.0 for code coverage). No worst value is set by default. 717 * 718 * @param d the worst value 719 * @return the builder 720 */ 721 public Builder setWorstValue(Double d) { 722 this.worstValue = d; 723 return this; 724 } 725 726 /** 727 * Sets the best value that the metric can get (example: 100.0 for code coverage). No best value is set by default. 728 * <br/> 729 * Resources would be hidden on drilldown page, if the value of measure equals to best value. 730 * 731 * @param d the best value 732 * @return the builder 733 */ 734 public Builder setBestValue(Double d) { 735 this.bestValue = d; 736 return this; 737 } 738 739 /** 740 * Specifies whether file-level measures that equal to the defined best value are stored or not. Default is false. 741 * <br/> 742 * Example with the metric that stores the number of violation ({@link CoreMetrics#VIOLATIONS}): 743 * if a file has no violation, then the value '0' won't be stored in the database. 744 * 745 * @param b true if the measures must not be stored when they equal to the best value 746 * @return the builder 747 */ 748 public Builder setOptimizedBestValue(boolean b) { 749 this.optimizedBestValue = b; 750 return this; 751 } 752 753 /** 754 * Sets whether the metric should be hidden in Web UI (e.g. in Time Machine). Default is false. 755 * 756 * @param b true if the metric should be hidden. 757 * @return the builder 758 */ 759 public Builder setHidden(boolean b) { 760 this.hidden = b; 761 return this; 762 } 763 764 /** 765 * Specifies whether this metric can be edited online in the "Manual measures" page. Default is false. 766 * 767 * @since 2.10 768 * 769 * @param b true if the metric can be edited online. 770 * @return the builder 771 */ 772 public Builder setUserManaged(boolean b) { 773 this.userManaged = b; 774 return this; 775 } 776 777 /** 778 * Specifies whether measures from the past can be automatically deleted to minimize database volume. 779 * <br/> 780 * By default, historical data are kept. 781 * 782 * @since 2.14 783 * 784 * @param b true if measures from the past can be deleted automatically. 785 * @return the builder 786 */ 787 public Builder setDeleteHistoricalData(boolean b) { 788 this.deleteHistoricalData = b; 789 return this; 790 } 791 792 /** 793 * Creates a new metric definition based on the properties set on this metric builder. 794 * 795 * @return a new {@link Metric} object 796 */ 797 public <G extends Serializable> Metric<G> create() { 798 if (ValueType.PERCENT.equals(this.type)) { 799 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); 800 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); 801 } 802 return new Metric<G>(this); 803 } 804 } 805 806 @Override 807 public String key() { 808 return getKey(); 809 } 810 811 @Override 812 public Class<G> valueType() { 813 return getType().valueType(); 814 } 815 }