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.annotations.Beta; 023import java.io.Serializable; 024import java.math.BigDecimal; 025import java.math.RoundingMode; 026import java.util.Date; 027import javax.annotation.CheckForNull; 028import javax.annotation.Nullable; 029import org.apache.commons.lang.builder.ReflectionToStringBuilder; 030import org.apache.commons.lang.math.NumberUtils; 031import org.sonar.api.technicaldebt.batch.Characteristic; 032import org.sonar.api.technicaldebt.batch.Requirement; 033 034/** 035 * A class to handle measures. 036 * 037 * @since 1.10 038 */ 039public class Measure<G extends Serializable> implements Serializable { 040 private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5"; 041 042 protected static final int MAX_TEXT_SIZE = 96; 043 044 /** 045 * Default precision when saving a float type metric 046 */ 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 * @param v the measure value 326 * @param precision the measure value precision 327 * @return the measure object instance 328 */ 329 public Measure<G> setValue(@Nullable Double v, int precision) { 330 if (v != null) { 331 if (Double.isNaN(v)) { 332 throw new IllegalArgumentException("Measure value can not be NaN"); 333 } 334 this.value = scaleValue(v, precision); 335 } else { 336 this.value = null; 337 } 338 return this; 339 } 340 341 private static double scaleValue(double value, int scale) { 342 BigDecimal bd = BigDecimal.valueOf(value); 343 return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue(); 344 } 345 346 /** 347 * @return the data field of the measure 348 */ 349 @CheckForNull 350 public String getData() { 351 return data; 352 } 353 354 /** 355 * Sets the data field of the measure. 356 * 357 * @param s the data 358 * @return the measure object instance 359 */ 360 public Measure<G> setData(String s) { 361 this.data = s; 362 return this; 363 } 364 365 /** 366 * Sets an alert level as the data field 367 * 368 * @param level the alert level 369 * @return the measure object instance 370 */ 371 public Measure<G> setData(Metric.Level level) { 372 if (level == null) { 373 this.data = null; 374 } else { 375 this.data = level.toString(); 376 } 377 return this; 378 } 379 380 /** 381 * @since 2.7 382 */ 383 public Measure<G> unsetData() { 384 this.data = null; 385 return this; 386 } 387 388 /** 389 * @return the description of the measure 390 */ 391 public String getDescription() { 392 return description; 393 } 394 395 /** 396 * Sets the measure description 397 * 398 * @param description the description 399 * @return the measure object instance 400 */ 401 public Measure<G> setDescription(String description) { 402 this.description = description; 403 return this; 404 } 405 406 /** 407 * @return the alert status of the measure 408 */ 409 public Metric.Level getAlertStatus() { 410 return alertStatus; 411 } 412 413 /** 414 * Set the alert status of the measure 415 * 416 * @param status the status 417 * @return the measure object instance 418 */ 419 public Measure<G> setAlertStatus(@Nullable Metric.Level status) { 420 this.alertStatus = status; 421 return this; 422 } 423 424 /** 425 * @return the text associated to the alert on the measure 426 */ 427 public String getAlertText() { 428 return alertText; 429 } 430 431 /** 432 * Sets the text associated to the alert on the measure 433 * 434 * @param alertText the text 435 * @return the measure object instance 436 */ 437 public Measure<G> setAlertText(@Nullable String alertText) { 438 this.alertText = alertText; 439 return this; 440 } 441 442 /** 443 * Concept of measure trend is dropped. 444 * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392 445 * @return {@code null} since version 5.2 446 */ 447 @Deprecated 448 @CheckForNull 449 public Integer getTendency() { 450 return null; 451 } 452 453 /** 454 * Concept of measure trend is dropped. This method does nothing. 455 * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392 456 * @return the measure object instance 457 */ 458 @Deprecated 459 public Measure<G> setTendency(@Nullable Integer tendency) { 460 return this; 461 } 462 463 /** 464 * Called by views when cloning measures 465 * @deprecated since 4.4 not used 466 */ 467 @Deprecated 468 public Measure<G> setId(Long id) { 469 return this; 470 } 471 472 /** 473 * @return the first variation value 474 * @since 2.5 475 */ 476 public Double getVariation1() { 477 return variation1; 478 } 479 480 /** 481 * Internal use only 482 * 483 * @since 2.5 484 */ 485 public Measure<G> setVariation1(@Nullable Double d) { 486 this.variation1 = d; 487 return this; 488 } 489 490 /** 491 * @return the second variation value 492 * @since 2.5 493 */ 494 public Double getVariation2() { 495 return variation2; 496 } 497 498 /** 499 * Internal use only 500 * 501 * @since 2.5 502 */ 503 public Measure<G> setVariation2(@Nullable Double d) { 504 this.variation2 = d; 505 return this; 506 } 507 508 /** 509 * @return the third variation value 510 * @since 2.5 511 */ 512 public Double getVariation3() { 513 return variation3; 514 } 515 516 /** 517 * Internal use only 518 * 519 * @since 2.5 520 */ 521 public Measure<G> setVariation3(@Nullable Double d) { 522 this.variation3 = d; 523 return this; 524 } 525 526 /** 527 * @return the third variation value 528 * @since 2.5 529 */ 530 public Double getVariation4() { 531 return variation4; 532 } 533 534 /** 535 * Internal use only 536 * 537 * @since 2.5 538 */ 539 public Measure<G> setVariation4(@Nullable Double d) { 540 this.variation4 = d; 541 return this; 542 } 543 544 /** 545 * @return the third variation value 546 * @since 2.5 547 */ 548 public Double getVariation5() { 549 return variation5; 550 } 551 552 /** 553 * Internal use only 554 * 555 * @since 2.5 556 */ 557 public Measure<G> setVariation5(@Nullable Double d) { 558 this.variation5 = d; 559 return this; 560 } 561 562 /** 563 * @since 2.5 564 */ 565 public Double getVariation(int index) { 566 switch (index) { 567 case 1: 568 return variation1; 569 case 2: 570 return variation2; 571 case 3: 572 return variation3; 573 case 4: 574 return variation4; 575 case 5: 576 return variation5; 577 default: 578 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 579 } 580 } 581 582 /** 583 * Internal use only 584 * 585 * @since 2.5 586 */ 587 public Measure<G> setVariation(int index, Double d) { 588 switch (index) { 589 case 1: 590 variation1 = d; 591 break; 592 case 2: 593 variation2 = d; 594 break; 595 case 3: 596 variation3 = d; 597 break; 598 case 4: 599 variation4 = d; 600 break; 601 case 5: 602 variation5 = d; 603 break; 604 default: 605 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 606 } 607 return this; 608 } 609 610 /** 611 * @return the url of the measure 612 */ 613 public String getUrl() { 614 return url; 615 } 616 617 /** 618 * Sets the URL of the measure 619 * 620 * @param url the url 621 * @return the measure object instance 622 */ 623 public Measure<G> setUrl(String url) { 624 this.url = url; 625 return this; 626 } 627 628 /** 629 * @since 4.1 630 */ 631 @CheckForNull 632 public final Characteristic getCharacteristic() { 633 return characteristic; 634 } 635 636 /** 637 * @since 4.1 638 */ 639 public final Measure<G> setCharacteristic(@Nullable Characteristic characteristic) { 640 this.characteristic = characteristic; 641 return this; 642 } 643 644 /** 645 * @since 4.1 646 * @deprecated since 4.3. 647 */ 648 @Deprecated 649 @CheckForNull 650 public final Requirement getRequirement() { 651 return requirement; 652 } 653 654 /** 655 * @since 4.1 656 * @deprecated since 4.3 657 */ 658 @Deprecated 659 public final Measure<G> setRequirement(@Nullable Requirement requirement) { 660 this.requirement = requirement; 661 return this; 662 } 663 664 /** 665 * @since 2.14 666 */ 667 @CheckForNull 668 @Beta 669 public Integer getPersonId() { 670 return personId; 671 } 672 673 /** 674 * @since 2.14 675 */ 676 @Beta 677 public Measure<G> setPersonId(@Nullable Integer i) { 678 this.personId = i; 679 return this; 680 } 681 682 /** 683 * @since 3.2 684 */ 685 public boolean isBestValue() { 686 Double bestValue = metric.getBestValue(); 687 return metric.isOptimizedBestValue() == Boolean.TRUE 688 && bestValue != null 689 && (value == null || NumberUtils.compare(bestValue, value) == 0) 690 && allNull(alertStatus, description, url, data) 691 && isZeroVariation(variation1, variation2, variation3, variation4, variation5); 692 } 693 694 /** 695 * For internal use 696 */ 697 public boolean isFromCore() { 698 return fromCore; 699 } 700 701 /** 702 * For internal use 703 */ 704 public void setFromCore(boolean fromCore) { 705 this.fromCore = fromCore; 706 } 707 708 private static boolean isZeroVariation(Double... variations) { 709 for (Double variation : variations) { 710 if (variation != null && NumberUtils.compare(variation, 0.0) != 0) { 711 return false; 712 } 713 } 714 return true; 715 } 716 717 private static boolean allNull(Object... values) { 718 for (Object value : values) { 719 if (null != value) { 720 return false; 721 } 722 } 723 return true; 724 } 725 726 @Override 727 public boolean equals(Object o) { 728 if (this == o) { 729 return true; 730 } 731 if (o == null || getClass() != o.getClass()) { 732 return false; 733 } 734 735 Measure measure = (Measure) o; 736 if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) { 737 return false; 738 } 739 if (characteristic != null ? !characteristic.equals(measure.characteristic) : (measure.characteristic != null)) { 740 return false; 741 } 742 return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null)); 743 } 744 745 @Override 746 public int hashCode() { 747 int result = metricKey != null ? metricKey.hashCode() : 0; 748 result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); 749 result = 31 * result + (personId != null ? personId.hashCode() : 0); 750 return result; 751 } 752 753 @Override 754 public String toString() { 755 return ReflectionToStringBuilder.toString(this); 756 } 757 758}