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