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.*; 031 032 /** 033 * This class represents the definition of a metric in Sonar. 034 * 035 * @since 1.10 036 */ 037 @Table(name = "metrics") 038 @Entity(name = "Metric") 039 @InstantiationStrategy(InstantiationStrategy.PER_BATCH) 040 public class Metric implements ServerExtension, BatchExtension { 041 042 /** 043 * A metric bigger value means a degradation 044 */ 045 public static final int DIRECTION_WORST = -1; 046 /** 047 * A metric bigger value means an improvement 048 */ 049 public static final int DIRECTION_BETTER = 1; 050 /** 051 * The metric direction has no meaning 052 */ 053 public static final int DIRECTION_NONE = 0; 054 055 public enum ValueType { 056 INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB, RATING, WORK_DUR 057 } 058 059 public enum Level { 060 OK("Green"), WARN("Orange"), ERROR("Red"); 061 062 private String colorName; 063 064 Level(String colorName) { 065 this.colorName = colorName; 066 } 067 068 public String getColorName() { 069 return colorName; 070 } 071 } 072 073 public enum Origin { 074 JAV, GUI, WS 075 } 076 077 @Id 078 @Column(name = "id") 079 @GeneratedValue 080 private Integer id; 081 082 @Transient 083 private Formula formula; 084 085 @Column(name = "name", updatable = false, nullable = false, length = 64) 086 private String key; 087 088 @Column(name = "description", updatable = true, nullable = true, length = 255) 089 private String description; 090 091 @Column(name = "val_type", updatable = true, nullable = true) 092 @Enumerated(EnumType.STRING) 093 private ValueType type; 094 095 @Column(name = "direction", updatable = true, nullable = true) 096 private Integer direction; 097 098 @Column(name = "domain", updatable = true, nullable = true, length = 60) 099 private String domain; 100 101 @Column(name = "short_name", updatable = true, nullable = true, length = 64) 102 private String name; 103 104 @Column(name = "qualitative", updatable = true, nullable = true) 105 private Boolean qualitative = Boolean.FALSE; 106 107 @Column(name = "user_managed", updatable = true, nullable = true) 108 private Boolean userManaged = Boolean.FALSE; 109 110 @Column(name = "enabled", updatable = true, nullable = true) 111 private Boolean enabled = Boolean.TRUE; 112 113 @Column(name = "origin", updatable = true, nullable = true, length = 3) 114 @Enumerated(EnumType.STRING) 115 private Origin origin = Origin.JAV; 116 117 @Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20) 118 private Double worstValue; 119 120 @Column(name = "best_value", updatable = true, nullable = true, precision = 30, scale = 20) 121 private Double bestValue; 122 123 @Column(name = "optimized_best_value", updatable = true, nullable = true) 124 private Boolean optimizedBestValue; 125 126 @Column(name = "hidden", updatable = true, nullable = true) 127 private Boolean hidden = Boolean.FALSE; 128 129 @Column(name = "delete_historical_data", updatable = true, nullable = true) 130 private Boolean deleteHistoricalData; 131 132 private Metric(Builder builder) { 133 this.key = builder.key; 134 this.name = builder.name; 135 this.description = builder.description; 136 this.type = builder.type; 137 this.direction = builder.direction; 138 this.domain = builder.domain; 139 this.qualitative = builder.qualitative; 140 this.enabled = Boolean.TRUE; 141 this.worstValue = builder.worstValue; 142 this.optimizedBestValue = builder.optimizedBestValue; 143 this.bestValue = builder.bestValue; 144 this.hidden = builder.hidden; 145 this.formula = builder.formula; 146 this.userManaged = builder.userManaged; 147 this.deleteHistoricalData = builder.deleteHistoricalData; 148 } 149 150 /** 151 * Creates an empty metric. Required for Hibernate. 152 * 153 * @deprecated in 1.12. Use the {@link Builder} factory. 154 */ 155 @Deprecated 156 public Metric() { 157 } 158 159 /** 160 * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT) 161 * 162 * @param key the metric key 163 * @deprecated since 2.7 use the {@link Builder} factory. 164 */ 165 @Deprecated 166 public Metric(String key) { 167 this(key, ValueType.INT); 168 } 169 170 /** 171 * Creates a metric based on a key and a type. Shortcut to 172 * Metric(key, key, key, type, -1, Boolean.FALSE, null, false) 173 * 174 * @param key the key 175 * @param type the type 176 * @deprecated since 2.7 use the {@link Builder} factory. 177 */ 178 @Deprecated 179 public Metric(String key, ValueType type) { 180 this(key, key, key, type, -1, Boolean.FALSE, null, false); 181 } 182 183 /** 184 * @deprecated since 2.7 use the {@link Builder} factory. 185 */ 186 @Deprecated 187 public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) { 188 this(key, name, description, type, direction, qualitative, domain, false); 189 } 190 191 /** 192 * Creates a fully qualified metric. This defaults some values: 193 * <ul> 194 * <li>origin : Origin.JAV</li> 195 * </ul> 196 * 197 * @param key the metric key 198 * @param name the metric name 199 * @param description the metric description 200 * @param type the metric type 201 * @param direction the metric direction 202 * @param qualitative whether the metric is qualitative 203 * @param domain the metric domain 204 * @param userManaged whether the metric is user managed 205 * @deprecated since 2.7 use the {@link Builder} factory. 206 */ 207 @Deprecated 208 public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain, 209 boolean userManaged) { 210 this.key = key; 211 this.description = description; 212 this.type = type; 213 this.direction = direction; 214 this.domain = domain; 215 this.name = name; 216 this.qualitative = qualitative; 217 this.userManaged = userManaged; 218 this.origin = Origin.JAV; 219 if (ValueType.PERCENT.equals(this.type)) { 220 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); 221 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); 222 } 223 } 224 225 /** 226 * Creates a fully qualified metric. This defaults some values: 227 * <ul> 228 * <li>origin : Origin.JAV</li> 229 * <li>enabled : true</li> 230 * <li>userManaged : true</li> 231 * </ul> 232 * 233 * @param key the metric key 234 * @param name the metric name 235 * @param type the metric type 236 * @param direction the metric direction 237 * @param qualitative whether the metric is qualitative 238 * @param domain the metric domain 239 * @param formula the metric formula 240 * @deprecated since 2.7 use the {@link Builder} factory. 241 */ 242 @Deprecated 243 public Metric(String key, String name, ValueType type, Integer direction, Boolean qualitative, String domain, Formula formula) { 244 this.key = key; 245 this.name = name; 246 this.type = type; 247 this.direction = direction; 248 this.domain = domain; 249 this.qualitative = qualitative; 250 this.origin = Origin.JAV; 251 this.enabled = true; 252 this.userManaged = false; 253 this.formula = formula; 254 if (ValueType.PERCENT.equals(this.type)) { 255 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0); 256 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0); 257 } 258 } 259 260 /** 261 * For internal use only 262 */ 263 public Integer getId() { 264 return id; 265 } 266 267 /** 268 * For internal use only 269 */ 270 public Metric setId(@Nullable Integer id) { 271 this.id = id; 272 return this; 273 } 274 275 /** 276 * @return the metric formula 277 */ 278 public Formula getFormula() { 279 return formula; 280 } 281 282 /** 283 * Sets the metric formula 284 * 285 * @param formula the formula 286 * @return this 287 */ 288 public Metric setFormula(Formula formula) { 289 this.formula = formula; 290 return this; 291 } 292 293 /** 294 * @return wether the metric is qualitative 295 */ 296 public Boolean getQualitative() { 297 return qualitative; 298 } 299 300 /** 301 * Sets whether the metric is qualitative 302 * 303 * @param qualitative whether the metric is qualitative 304 * @return this 305 */ 306 public Metric setQualitative(Boolean qualitative) { 307 this.qualitative = qualitative; 308 return this; 309 } 310 311 /** 312 * @return the metric key 313 */ 314 public String getKey() { 315 return key; 316 } 317 318 /** 319 * Sets the metric key 320 * 321 * @param key the key 322 * @return this 323 */ 324 public Metric setKey(String key) { 325 this.key = key; 326 return this; 327 } 328 329 /** 330 * @return the metric type 331 */ 332 public ValueType getType() { 333 return type; 334 } 335 336 /** 337 * Sets the metric type 338 * 339 * @param type the type 340 * @return this 341 */ 342 public Metric setType(ValueType type) { 343 this.type = type; 344 return this; 345 } 346 347 /** 348 * @return the metric description 349 */ 350 public String getDescription() { 351 return description; 352 } 353 354 /** 355 * Sets the metric description 356 * 357 * @param description the description 358 * @return this 359 */ 360 public Metric setDescription(String description) { 361 this.description = description; 362 return this; 363 } 364 365 /** 366 * @return whether the metric is a managed by the users ("manual metric") 367 */ 368 public Boolean getUserManaged() { 369 return userManaged; 370 } 371 372 /** 373 * Sets whether the metric is managed by users ("manual metric") 374 * 375 * @param userManaged whether the metric is user managed 376 * @return this 377 */ 378 public Metric setUserManaged(Boolean userManaged) { 379 this.userManaged = userManaged; 380 return this; 381 } 382 383 /** 384 * @return whether the metric is enabled 385 */ 386 public Boolean getEnabled() { 387 return enabled; 388 } 389 390 /** 391 * Sets whether the metric is enabled 392 * 393 * @param enabled whether the metric is enabled 394 * @return this 395 */ 396 public Metric setEnabled(Boolean enabled) { 397 this.enabled = enabled; 398 return this; 399 } 400 401 /** 402 * @return the metric direction 403 */ 404 public Integer getDirection() { 405 return direction; 406 } 407 408 /** 409 * Sets the metric direction. 410 * 411 * @param direction the direction 412 */ 413 public Metric setDirection(Integer direction) { 414 this.direction = direction; 415 return this; 416 } 417 418 /** 419 * @return the domain of the metric 420 */ 421 public String getDomain() { 422 return domain; 423 } 424 425 /** 426 * Sets the domain for the metric (General, Complexity...) 427 * 428 * @param domain the domain 429 * @return this 430 */ 431 public Metric setDomain(String domain) { 432 this.domain = domain; 433 return this; 434 } 435 436 /** 437 * @return the metric name 438 */ 439 public String getName() { 440 return name; 441 } 442 443 /** 444 * Sets the metric name 445 * 446 * @param name the name 447 * @return this 448 */ 449 public Metric setName(String name) { 450 this.name = name; 451 return this; 452 } 453 454 /** 455 * @return the origin of the metric - Internal use only 456 */ 457 public Origin getOrigin() { 458 return origin; 459 } 460 461 /** 462 * Set the origin of the metric - Internal use only 463 * 464 * @param origin the origin 465 * @return this 466 */ 467 public Metric setOrigin(Origin origin) { 468 this.origin = origin; 469 return this; 470 } 471 472 public Double getWorstValue() { 473 return worstValue; 474 } 475 476 public Double getBestValue() { 477 return bestValue; 478 } 479 480 /** 481 * @return this 482 */ 483 public Metric setWorstValue(Double d) { 484 this.worstValue = d; 485 return this; 486 } 487 488 /** 489 * @param bestValue the best value. It can be null. 490 * @return this 491 */ 492 public Metric setBestValue(Double bestValue) { 493 this.bestValue = bestValue; 494 return this; 495 } 496 497 /** 498 * @return whether the metric is of a numeric type (int, percentage...) 499 */ 500 public boolean isNumericType() { 501 return ValueType.INT.equals(type) 502 || ValueType.FLOAT.equals(type) 503 || ValueType.PERCENT.equals(type) 504 || ValueType.BOOL.equals(type) 505 || ValueType.MILLISEC.equals(type) 506 || ValueType.RATING.equals(type) 507 || ValueType.WORK_DUR.equals(type); 508 } 509 510 /** 511 * @return whether the metric is of type data 512 */ 513 public boolean isDataType() { 514 return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type); 515 } 516 517 /** 518 * @return whether the metric is of type percentage 519 */ 520 public boolean isPercentageType() { 521 return ValueType.PERCENT.equals(type); 522 } 523 524 public Metric setOptimizedBestValue(Boolean b) { 525 this.optimizedBestValue = b; 526 return this; 527 } 528 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 Metric 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(this); 803 } 804 } 805 }