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