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