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