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