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