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