001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program 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 * This program 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.annotations.Beta; 023import java.io.Serializable; 024import java.util.Date; 025import javax.annotation.CheckForNull; 026import javax.annotation.Nullable; 027import org.apache.commons.lang.builder.ReflectionToStringBuilder; 028import org.apache.commons.lang.math.NumberUtils; 029import org.sonar.api.technicaldebt.batch.Characteristic; 030import org.sonar.api.technicaldebt.batch.Requirement; 031 032/** 033 * A class to handle measures. 034 * 035 * @since 1.10 036 */ 037public class Measure<G extends Serializable> implements Serializable { 038 private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5"; 039 040 protected static final int MAX_TEXT_SIZE = 96; 041 042 /** 043 * Default precision when saving a float type metric 044 * @deprecated in 5.3. Decimal scale is provided by metric, not by measure. 045 */ 046 @Deprecated 047 public static final int DEFAULT_PRECISION = 1; 048 049 protected String metricKey; 050 protected Metric<G> metric; 051 protected Double value; 052 protected String data; 053 protected String description; 054 protected Metric.Level alertStatus; 055 protected String alertText; 056 protected Date date; 057 protected Double variation1; 058 protected Double variation2; 059 protected Double variation3; 060 protected Double variation4; 061 protected Double variation5; 062 protected String url; 063 protected Characteristic characteristic; 064 protected Requirement requirement; 065 protected Integer personId; 066 protected PersistenceMode persistenceMode = PersistenceMode.FULL; 067 private boolean fromCore; 068 069 public Measure(String metricKey) { 070 this.metricKey = metricKey; 071 } 072 073 /** 074 * Creates a measure with a metric 075 * 076 * @param metric the metric 077 */ 078 public Measure(Metric metric) { 079 this.metric = metric; 080 this.metricKey = metric.getKey(); 081 } 082 083 /** 084 * Creates a measure with a metric and a value 085 * 086 * @param metric the metric 087 * @param value its value 088 */ 089 public Measure(Metric metric, Double value) { 090 this.metric = metric; 091 this.metricKey = metric.getKey(); 092 setValue(value); 093 } 094 095 /** 096 * Creates a measure with a metric, a value and a precision for the value 097 * 098 * @param metric the metric 099 * @param value its value 100 * @param precision the value precision 101 */ 102 public Measure(Metric metric, Double value, int precision) { 103 this.metric = metric; 104 this.metricKey = metric.getKey(); 105 setValue(value, precision); 106 } 107 108 /** 109 * Creates a measure with a metric, a value and a data field 110 * 111 * @param metric the metric 112 * @param value the value 113 * @param data the data field 114 */ 115 public Measure(Metric metric, Double value, String data) { 116 this.metric = metric; 117 this.metricKey = metric.getKey(); 118 setValue(value); 119 setData(data); 120 } 121 122 /** 123 * * Creates a measure with a metric and a data field 124 * 125 * @param metric the metric 126 * @param data the data field 127 */ 128 public Measure(Metric metric, String data) { 129 this.metric = metric; 130 this.metricKey = metric.getKey(); 131 setData(data); 132 } 133 134 /** 135 * Creates a measure with a metric and an alert level 136 * 137 * @param metric the metric 138 * @param level the alert level 139 */ 140 public Measure(Metric metric, Metric.Level level) { 141 this.metric = metric; 142 this.metricKey = metric.getKey(); 143 if (level != null) { 144 this.data = level.toString(); 145 } 146 } 147 148 /** 149 * Creates an empty measure 150 */ 151 public Measure() { 152 } 153 154 /** 155 * Gets the persistence mode of the measure. Default persistence mode is FULL, except when instantiating the measure with a String 156 * parameter. 157 */ 158 public PersistenceMode getPersistenceMode() { 159 return persistenceMode; 160 } 161 162 /** 163 * <p> 164 * Sets the persistence mode of a measure. 165 * </p> 166 * <p> 167 * <b>WARNING : </b>Being able to reuse measures saved in memory is only possible within the same tree. In a multi-module project for 168 * example, a measure save in memory at the module level will not be accessible by the root project. In that case, database should be 169 * used. 170 * </p> 171 * 172 * @param mode the mode 173 * @return the measure object instance 174 */ 175 public Measure<G> setPersistenceMode(@Nullable PersistenceMode mode) { 176 if (mode == null) { 177 this.persistenceMode = PersistenceMode.FULL; 178 } else { 179 this.persistenceMode = mode; 180 } 181 return this; 182 } 183 184 /** 185 * @return return the measures underlying metric 186 */ 187 public Metric<G> getMetric() { 188 return metric; 189 } 190 191 public String getMetricKey() { 192 return metricKey; 193 } 194 195 /** 196 * Set the underlying metric 197 * 198 * @param metric the metric 199 * @return the measure object instance 200 */ 201 public Measure<G> setMetric(Metric<G> metric) { 202 this.metric = metric; 203 this.metricKey = metric.getKey(); 204 return this; 205 } 206 207 /** 208 * @return transforms and returns the data fields as a level of alert 209 */ 210 public Metric.Level getDataAsLevel() { 211 if (data != null) { 212 return Metric.Level.valueOf(data); 213 } 214 return null; 215 } 216 217 public boolean hasData() { 218 return data != null; 219 } 220 221 /** 222 * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries 223 */ 224 public Date getDate() { 225 return date; 226 } 227 228 /** 229 * Sets the date of the measure - Used only in TimeMachine queries 230 * 231 * @param date the date 232 * @return the measure object instance 233 */ 234 public Measure<G> setDate(Date date) { 235 this.date = date; 236 return this; 237 } 238 239 /** 240 * @return the value of the measure as a double 241 */ 242 @CheckForNull 243 public Double getValue() { 244 return value; 245 } 246 247 /** 248 * For internal use. 249 */ 250 public G value() { 251 switch (getMetric().getType()) { 252 case BOOL: 253 return value == null ? null : (G) Boolean.valueOf(Double.doubleToRawLongBits(value) != 0L); 254 case INT: 255 case MILLISEC: 256 case RATING: 257 return value == null ? null : (G) Integer.valueOf(value.intValue()); 258 case FLOAT: 259 case PERCENT: 260 return value == null ? null : (G) value; 261 case STRING: 262 case LEVEL: 263 case DATA: 264 case DISTRIB: 265 return data == null ? null : (G) data; 266 case WORK_DUR: 267 return value == null ? null : (G) Long.valueOf(value.longValue()); 268 default: 269 if (getMetric().isNumericType()) { 270 return value == null ? null : (G) value; 271 } else if (getMetric().isDataType()) { 272 return data == null ? null : (G) data; 273 } else { 274 throw new UnsupportedOperationException("Unsupported type :" + getMetric().getType()); 275 } 276 } 277 } 278 279 /** 280 * @return the value of the measure as an int 281 */ 282 public Integer getIntValue() { 283 if (value == null) { 284 return null; 285 } 286 return value.intValue(); 287 } 288 289 /** 290 * Sets the measure value with the default precision of 1 291 * 292 * @param v the measure value 293 * @return the measure object instance 294 */ 295 public Measure<G> setValue(@Nullable Double v) { 296 return setValue(v, DEFAULT_PRECISION); 297 } 298 299 /** 300 * For internal use 301 */ 302 public Measure<G> setRawValue(@Nullable Double v) { 303 this.value = v; 304 return this; 305 } 306 307 /** 308 * Sets the measure value as an int 309 * 310 * @param i the value 311 * @return the measure object instance 312 */ 313 public Measure<G> setIntValue(Integer i) { 314 if (i == null) { 315 this.value = null; 316 } else { 317 this.value = Double.valueOf(i); 318 } 319 return this; 320 } 321 322 /** 323 * Sets the measure value with a given precision 324 * 325 * @return {@code this} 326 * @deprecated in 5.3. The decimal scale is given by the metric, not by the measure. Anyway this parameter was enforced to 1 before version 5.3. 327 */ 328 @Deprecated 329 public Measure<G> setValue(@Nullable Double v, int decimalScale) { 330 if (v != null) { 331 if (Double.isNaN(v)) { 332 throw new IllegalArgumentException("Measure value can not be NaN"); 333 } 334 this.value = v; 335 } else { 336 this.value = null; 337 } 338 return this; 339 } 340 341 /** 342 * @return the data field of the measure 343 */ 344 @CheckForNull 345 public String getData() { 346 return data; 347 } 348 349 /** 350 * Sets the data field of the measure. 351 * 352 * @param s the data 353 * @return the measure object instance 354 */ 355 public Measure<G> setData(String s) { 356 this.data = s; 357 return this; 358 } 359 360 /** 361 * Sets an alert level as the data field 362 * 363 * @param level the alert level 364 * @return the measure object instance 365 */ 366 public Measure<G> setData(Metric.Level level) { 367 if (level == null) { 368 this.data = null; 369 } else { 370 this.data = level.toString(); 371 } 372 return this; 373 } 374 375 /** 376 * @since 2.7 377 */ 378 public Measure<G> unsetData() { 379 this.data = null; 380 return this; 381 } 382 383 /** 384 * @return the description of the measure 385 */ 386 public String getDescription() { 387 return description; 388 } 389 390 /** 391 * Sets the measure description 392 * 393 * @param description the description 394 * @return the measure object instance 395 */ 396 public Measure<G> setDescription(String description) { 397 this.description = description; 398 return this; 399 } 400 401 /** 402 * @return the alert status of the measure 403 */ 404 public Metric.Level getAlertStatus() { 405 return alertStatus; 406 } 407 408 /** 409 * Set the alert status of the measure 410 * 411 * @param status the status 412 * @return the measure object instance 413 */ 414 public Measure<G> setAlertStatus(@Nullable Metric.Level status) { 415 this.alertStatus = status; 416 return this; 417 } 418 419 /** 420 * @return the text associated to the alert on the measure 421 */ 422 public String getAlertText() { 423 return alertText; 424 } 425 426 /** 427 * Sets the text associated to the alert on the measure 428 * 429 * @param alertText the text 430 * @return the measure object instance 431 */ 432 public Measure<G> setAlertText(@Nullable String alertText) { 433 this.alertText = alertText; 434 return this; 435 } 436 437 /** 438 * Concept of measure trend is dropped. 439 * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392 440 * @return {@code null} since version 5.2 441 */ 442 @Deprecated 443 @CheckForNull 444 public Integer getTendency() { 445 return null; 446 } 447 448 /** 449 * Concept of measure trend is dropped. This method does nothing. 450 * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392 451 * @return the measure object instance 452 */ 453 @Deprecated 454 public Measure<G> setTendency(@Nullable Integer tendency) { 455 return this; 456 } 457 458 /** 459 * Called by views when cloning measures 460 * @deprecated since 4.4 not used 461 */ 462 @Deprecated 463 public Measure<G> setId(Long id) { 464 return this; 465 } 466 467 /** 468 * @return the first variation value 469 * @since 2.5 470 */ 471 public Double getVariation1() { 472 return variation1; 473 } 474 475 /** 476 * Internal use only 477 * 478 * @since 2.5 479 */ 480 public Measure<G> setVariation1(@Nullable Double d) { 481 this.variation1 = d; 482 return this; 483 } 484 485 /** 486 * @return the second variation value 487 * @since 2.5 488 */ 489 public Double getVariation2() { 490 return variation2; 491 } 492 493 /** 494 * Internal use only 495 * 496 * @since 2.5 497 */ 498 public Measure<G> setVariation2(@Nullable Double d) { 499 this.variation2 = d; 500 return this; 501 } 502 503 /** 504 * @return the third variation value 505 * @since 2.5 506 */ 507 public Double getVariation3() { 508 return variation3; 509 } 510 511 /** 512 * Internal use only 513 * 514 * @since 2.5 515 */ 516 public Measure<G> setVariation3(@Nullable Double d) { 517 this.variation3 = d; 518 return this; 519 } 520 521 /** 522 * @return the third variation value 523 * @since 2.5 524 */ 525 public Double getVariation4() { 526 return variation4; 527 } 528 529 /** 530 * Internal use only 531 * 532 * @since 2.5 533 */ 534 public Measure<G> setVariation4(@Nullable Double d) { 535 this.variation4 = d; 536 return this; 537 } 538 539 /** 540 * @return the third variation value 541 * @since 2.5 542 */ 543 public Double getVariation5() { 544 return variation5; 545 } 546 547 /** 548 * Internal use only 549 * 550 * @since 2.5 551 */ 552 public Measure<G> setVariation5(@Nullable Double d) { 553 this.variation5 = d; 554 return this; 555 } 556 557 /** 558 * @since 2.5 559 */ 560 public Double getVariation(int index) { 561 switch (index) { 562 case 1: 563 return variation1; 564 case 2: 565 return variation2; 566 case 3: 567 return variation3; 568 case 4: 569 return variation4; 570 case 5: 571 return variation5; 572 default: 573 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 574 } 575 } 576 577 /** 578 * Internal use only 579 * 580 * @since 2.5 581 */ 582 public Measure<G> setVariation(int index, Double d) { 583 switch (index) { 584 case 1: 585 variation1 = d; 586 break; 587 case 2: 588 variation2 = d; 589 break; 590 case 3: 591 variation3 = d; 592 break; 593 case 4: 594 variation4 = d; 595 break; 596 case 5: 597 variation5 = d; 598 break; 599 default: 600 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 601 } 602 return this; 603 } 604 605 /** 606 * @return the url of the measure 607 */ 608 public String getUrl() { 609 return url; 610 } 611 612 /** 613 * Sets the URL of the measure 614 * 615 * @param url the url 616 * @return the measure object instance 617 */ 618 public Measure<G> setUrl(String url) { 619 this.url = url; 620 return this; 621 } 622 623 /** 624 * @since 4.1 625 */ 626 @CheckForNull 627 public final Characteristic getCharacteristic() { 628 return characteristic; 629 } 630 631 /** 632 * @since 4.1 633 */ 634 public final Measure<G> setCharacteristic(@Nullable Characteristic characteristic) { 635 this.characteristic = characteristic; 636 return this; 637 } 638 639 /** 640 * @since 4.1 641 * @deprecated since 4.3. 642 */ 643 @Deprecated 644 @CheckForNull 645 public final Requirement getRequirement() { 646 return requirement; 647 } 648 649 /** 650 * @since 4.1 651 * @deprecated since 4.3 652 */ 653 @Deprecated 654 public final Measure<G> setRequirement(@Nullable Requirement requirement) { 655 this.requirement = requirement; 656 return this; 657 } 658 659 /** 660 * @since 2.14 661 */ 662 @CheckForNull 663 @Beta 664 public Integer getPersonId() { 665 return personId; 666 } 667 668 /** 669 * @since 2.14 670 */ 671 @Beta 672 public Measure<G> setPersonId(@Nullable Integer i) { 673 this.personId = i; 674 return this; 675 } 676 677 /** 678 * @since 3.2 679 */ 680 public boolean isBestValue() { 681 Double bestValue = metric.getBestValue(); 682 return metric.isOptimizedBestValue() == Boolean.TRUE 683 && bestValue != null 684 && (value == null || NumberUtils.compare(bestValue, value) == 0) 685 && allNull(alertStatus, description, url, data) 686 && isZeroVariation(variation1, variation2, variation3, variation4, variation5); 687 } 688 689 /** 690 * For internal use 691 */ 692 public boolean isFromCore() { 693 return fromCore; 694 } 695 696 /** 697 * For internal use 698 */ 699 public void setFromCore(boolean fromCore) { 700 this.fromCore = fromCore; 701 } 702 703 private static boolean isZeroVariation(Double... variations) { 704 for (Double variation : variations) { 705 if (variation != null && NumberUtils.compare(variation, 0.0) != 0) { 706 return false; 707 } 708 } 709 return true; 710 } 711 712 private static boolean allNull(Object... values) { 713 for (Object value : values) { 714 if (null != value) { 715 return false; 716 } 717 } 718 return true; 719 } 720 721 @Override 722 public boolean equals(Object o) { 723 if (this == o) { 724 return true; 725 } 726 if (o == null || getClass() != o.getClass()) { 727 return false; 728 } 729 730 Measure measure = (Measure) o; 731 if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) { 732 return false; 733 } 734 if (characteristic != null ? !characteristic.equals(measure.characteristic) : (measure.characteristic != null)) { 735 return false; 736 } 737 return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null)); 738 } 739 740 @Override 741 public int hashCode() { 742 int result = metricKey != null ? metricKey.hashCode() : 0; 743 result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); 744 result = 31 * result + (personId != null ? personId.hashCode() : 0); 745 return result; 746 } 747 748 @Override 749 public String toString() { 750 return ReflectionToStringBuilder.toString(this); 751 } 752 753}