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 public String getData() { 347 return data; 348 } 349 350 /** 351 * Sets the data field of the measure. 352 * 353 * @param s the data 354 * @return the measure object instance 355 */ 356 public Measure setData(String s) { 357 this.data = s; 358 return this; 359 } 360 361 /** 362 * Sets an alert level as the data field 363 * 364 * @param level the alert level 365 * @return the measure object instance 366 */ 367 public Measure setData(Metric.Level level) { 368 if (level == null) { 369 this.data = null; 370 } else { 371 this.data = level.toString(); 372 } 373 return this; 374 } 375 376 /** 377 * @since 2.7 378 */ 379 public Measure unsetData() { 380 this.data = null; 381 return this; 382 } 383 384 /** 385 * @return the description of the measure 386 */ 387 public String getDescription() { 388 return description; 389 } 390 391 /** 392 * Sets the measure description 393 * 394 * @param description the description 395 * @return the measure object instance 396 */ 397 public Measure setDescription(String description) { 398 this.description = description; 399 return this; 400 } 401 402 /** 403 * @return the alert status of the measure 404 */ 405 public Metric.Level getAlertStatus() { 406 return alertStatus; 407 } 408 409 /** 410 * Set the alert status of the measure 411 * 412 * @param status the status 413 * @return the measure object instance 414 */ 415 public Measure setAlertStatus(@Nullable Metric.Level status) { 416 this.alertStatus = status; 417 return this; 418 } 419 420 /** 421 * @return the text associated to the alert on the measure 422 */ 423 public String getAlertText() { 424 return alertText; 425 } 426 427 /** 428 * Sets the text associated to the alert on the measure 429 * 430 * @param alertText the text 431 * @return the measure object instance 432 */ 433 public Measure setAlertText(@Nullable String alertText) { 434 this.alertText = alertText; 435 return this; 436 } 437 438 /** 439 * Gets the measure tendency 440 * 441 * @return the tendency 442 */ 443 public Integer getTendency() { 444 return tendency; 445 } 446 447 /** 448 * Sets the tendency for the measure - Internal use only 449 * 450 * @param tendency the tendency 451 * @return the measure object instance 452 */ 453 public Measure setTendency(@Nullable Integer tendency) { 454 this.tendency = 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 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 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 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 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 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 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 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 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 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 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 setPersonId(@Nullable Integer i) { 673 this.personId = i; 674 return this; 675 } 676 677 /** 678 * @since 3.2 679 */ 680 public boolean isBestValue() { 681 return metric.isOptimizedBestValue() == Boolean.TRUE 682 && metric.getBestValue() != null 683 && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0) 684 && allNull(alertStatus, description, tendency, url, data) 685 && isZeroVariation(variation1, variation2, variation3, variation4, variation5); 686 } 687 688 private static boolean isZeroVariation(Double... variations) { 689 for (Double variation : variations) { 690 if (!((variation == null) || NumberUtils.compare(variation, 0.0) == 0)) { 691 return false; 692 } 693 } 694 return true; 695 } 696 697 private static boolean allNull(Object... values) { 698 for (Object value : values) { 699 if (null != value) { 700 return false; 701 } 702 } 703 return true; 704 } 705 706 @Override 707 public boolean equals(Object o) { 708 if (this == o) { 709 return true; 710 } 711 if (o == null || getClass() != o.getClass()) { 712 return false; 713 } 714 715 Measure measure = (Measure) o; 716 if (metricKey != null ? !metricKey.equals(measure.metricKey) : measure.metricKey != null) { 717 return false; 718 } 719 if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) { 720 return false; 721 } 722 if (personId != null ? !personId.equals(measure.personId) : measure.personId != null) { 723 return false; 724 } 725 return true; 726 } 727 728 @Override 729 public int hashCode() { 730 int result = metricKey != null ? metricKey.hashCode() : 0; 731 result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); 732 result = 31 * result + (personId != null ? personId.hashCode() : 0); 733 return result; 734 } 735 736 @Override 737 public String toString() { 738 return ReflectionToStringBuilder.toString(this); 739 } 740 741 }