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