001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2013 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.qualitymodel.Characteristic; 026 027import javax.annotation.Nullable; 028 029import java.math.BigDecimal; 030import java.math.RoundingMode; 031import java.util.Date; 032 033/** 034 * A class to handle measures. 035 * 036 * @since 1.10 037 */ 038public class Measure { 039 private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5"; 040 041 protected static final int MAX_TEXT_SIZE = 96; 042 043 /** 044 * Default precision when saving a float type metric 045 */ 046 public static final int DEFAULT_PRECISION = 1; 047 048 // for internal use 049 private Long id; 050 protected String metricKey; 051 protected Metric metric; 052 protected Double value; 053 protected String data; 054 protected String description; 055 protected Metric.Level alertStatus; 056 protected String alertText; 057 protected Integer tendency; 058 protected Date date; 059 protected Double variation1, variation2, variation3, variation4, variation5; 060 protected String url; 061 protected Characteristic characteristic; 062 protected Integer personId; 063 protected PersistenceMode persistenceMode = PersistenceMode.FULL; 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 * </p> 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 * </p> 167 * 168 * @param mode the mode 169 * @return the measure object instance 170 */ 171 public Measure setPersistenceMode(PersistenceMode mode) { 172 this.persistenceMode = mode; 173 return this; 174 } 175 176 /** 177 * @return return the measures underlying metric 178 */ 179 public Metric getMetric() { 180 return metric; 181 } 182 183 public String getMetricKey() { 184 return metricKey; 185 } 186 187 /** 188 * Set the underlying metric 189 * 190 * @param metric the metric 191 * @return the measure object instance 192 */ 193 public Measure setMetric(Metric metric) { 194 this.metric = metric; 195 this.metricKey = metric.getKey(); 196 return this; 197 } 198 199 /** 200 * @return transforms and returns the data fields as a level of alert 201 */ 202 public Metric.Level getDataAsLevel() { 203 if (data != null) { 204 return Metric.Level.valueOf(data); 205 } 206 return null; 207 } 208 209 public boolean hasData() { 210 return data != null; 211 } 212 213 /** 214 * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries 215 */ 216 public Date getDate() { 217 return date; 218 } 219 220 /** 221 * Sets the date of the measure - Used only in TimeMachine queries 222 * 223 * @param date the date 224 * @return the measure object instance 225 */ 226 public Measure setDate(Date date) { 227 this.date = date; 228 return this; 229 } 230 231 /** 232 * @return the value of the measure as a double 233 */ 234 public Double getValue() { 235 return value; 236 } 237 238 /** 239 * @return the value of the measure as an int 240 */ 241 public Integer getIntValue() { 242 if (value == null) { 243 return null; 244 } 245 return value.intValue(); 246 } 247 248 /** 249 * Sets the measure value with the default precision of 1 250 * 251 * @param v the measure value 252 * @return the measure object instance 253 */ 254 public Measure setValue(@Nullable Double v) { 255 return setValue(v, DEFAULT_PRECISION); 256 } 257 258 /** 259 * Sets the measure value as an int 260 * 261 * @param i the value 262 * @return the measure object instance 263 */ 264 public Measure setIntValue(Integer i) { 265 if (i == null) { 266 this.value = null; 267 } else { 268 this.value = Double.valueOf(i); 269 } 270 return this; 271 } 272 273 /** 274 * Sets the measure value with a given precision 275 * 276 * @param v the measure value 277 * @param precision the measure value precision 278 * @return the measure object instance 279 */ 280 public Measure setValue(@Nullable Double v, int precision) { 281 if (v != null) { 282 if (Double.isNaN(v)) { 283 throw new IllegalArgumentException("Measure value can not be NaN"); 284 } 285 this.value = scaleValue(v, precision); 286 } else { 287 this.value = null; 288 } 289 return this; 290 } 291 292 private double scaleValue(double value, int scale) { 293 BigDecimal bd = BigDecimal.valueOf(value); 294 return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue(); 295 } 296 297 /** 298 * @return the data field of the measure 299 */ 300 public String getData() { 301 return data; 302 } 303 304 /** 305 * Sets the data field of the measure. 306 * 307 * @param s the data 308 * @return the measure object instance 309 */ 310 public Measure setData(String s) { 311 this.data = s; 312 return this; 313 } 314 315 /** 316 * Sets an alert level as the data field 317 * 318 * @param level the alert level 319 * @return the measure object instance 320 */ 321 public Measure setData(Metric.Level level) { 322 if (level == null) { 323 this.data = null; 324 } else { 325 this.data = level.toString(); 326 } 327 return this; 328 } 329 330 /** 331 * @since 2.7 332 */ 333 public Measure unsetData() { 334 this.data = null; 335 return this; 336 } 337 338 /** 339 * @return the description of the measure 340 */ 341 public String getDescription() { 342 return description; 343 } 344 345 /** 346 * Sets the measure description 347 * 348 * @param description the description 349 * @return the measure object instance 350 */ 351 public Measure setDescription(String description) { 352 this.description = description; 353 return this; 354 } 355 356 /** 357 * @return the alert status of the measure 358 */ 359 public Metric.Level getAlertStatus() { 360 return alertStatus; 361 } 362 363 /** 364 * Set the alert status of the measure 365 * 366 * @param status the status 367 * @return the measure object instance 368 */ 369 public Measure setAlertStatus(Metric.Level status) { 370 this.alertStatus = status; 371 return this; 372 } 373 374 /** 375 * @return the text associated to the alert on the measure 376 */ 377 public String getAlertText() { 378 return alertText; 379 } 380 381 /** 382 * Sets the text associated to the alert on the measure 383 * 384 * @param alertText the text 385 * @return the measure object instance 386 */ 387 public Measure setAlertText(String alertText) { 388 this.alertText = alertText; 389 return this; 390 } 391 392 /** 393 * Gets the measure tendency 394 * 395 * @return the tendency 396 */ 397 public Integer getTendency() { 398 return tendency; 399 } 400 401 /** 402 * Sets the tendency for the measure - Internal use only 403 * 404 * @param tendency the tendency 405 * @return the measure object instance 406 */ 407 public Measure setTendency(Integer tendency) { 408 this.tendency = tendency; 409 return this; 410 } 411 412 /** 413 * @return the measure id - Internal use only 414 */ 415 public Long getId() { 416 return id; 417 } 418 419 /** 420 * Sets the measure id - Internal use only 421 * 422 * @param id the id 423 * @return the measure object instance 424 */ 425 public Measure setId(Long id) { 426 this.id = id; 427 return this; 428 } 429 430 /** 431 * @return the first variation value 432 * @since 2.5 433 */ 434 public Double getVariation1() { 435 return variation1; 436 } 437 438 /** 439 * Internal use only 440 * 441 * @since 2.5 442 */ 443 public Measure setVariation1(Double d) { 444 this.variation1 = d; 445 return this; 446 } 447 448 /** 449 * @return the second variation value 450 * @since 2.5 451 */ 452 public Double getVariation2() { 453 return variation2; 454 } 455 456 /** 457 * Internal use only 458 * 459 * @since 2.5 460 */ 461 public Measure setVariation2(Double d) { 462 this.variation2 = d; 463 return this; 464 } 465 466 /** 467 * @return the third variation value 468 * @since 2.5 469 */ 470 public Double getVariation3() { 471 return variation3; 472 } 473 474 /** 475 * Internal use only 476 * 477 * @since 2.5 478 */ 479 public Measure setVariation3(Double d) { 480 this.variation3 = d; 481 return this; 482 } 483 484 /** 485 * @return the third variation value 486 * @since 2.5 487 */ 488 public Double getVariation4() { 489 return variation4; 490 } 491 492 /** 493 * Internal use only 494 * 495 * @since 2.5 496 */ 497 public Measure setVariation4(Double d) { 498 this.variation4 = d; 499 return this; 500 } 501 502 /** 503 * @return the third variation value 504 * @since 2.5 505 */ 506 public Double getVariation5() { 507 return variation5; 508 } 509 510 /** 511 * Internal use only 512 * 513 * @since 2.5 514 */ 515 public Measure setVariation5(Double d) { 516 this.variation5 = d; 517 return this; 518 } 519 520 /** 521 * @since 2.5 522 */ 523 public Double getVariation(int index) { 524 switch (index) { 525 case 1: 526 return variation1; 527 case 2: 528 return variation2; 529 case 3: 530 return variation3; 531 case 4: 532 return variation4; 533 case 5: 534 return variation5; 535 default: 536 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 537 } 538 } 539 540 /** 541 * Internal use only 542 * 543 * @since 2.5 544 */ 545 public Measure setVariation(int index, Double d) { 546 switch (index) { 547 case 1: 548 variation1 = d; 549 break; 550 case 2: 551 variation2 = d; 552 break; 553 case 3: 554 variation3 = d; 555 break; 556 case 4: 557 variation4 = d; 558 break; 559 case 5: 560 variation5 = d; 561 break; 562 default: 563 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5); 564 } 565 return this; 566 } 567 568 /** 569 * @return the url of the measure 570 */ 571 public String getUrl() { 572 return url; 573 } 574 575 /** 576 * Sets the URL of the measure 577 * 578 * @param url the url 579 * @return the measure object instance 580 */ 581 public Measure setUrl(String url) { 582 this.url = url; 583 return this; 584 } 585 586 /** 587 * @since 2.3 588 */ 589 public final Characteristic getCharacteristic() { 590 return characteristic; 591 } 592 593 /** 594 * @since 2.3 595 */ 596 public final Measure setCharacteristic(Characteristic characteristic) { 597 this.characteristic = characteristic; 598 return this; 599 } 600 601 /** 602 * @since 2.14 603 */ 604 @Beta 605 public Integer getPersonId() { 606 return personId; 607 } 608 609 /** 610 * @since 2.14 611 */ 612 @Beta 613 public Measure setPersonId(Integer i) { 614 this.personId = i; 615 return this; 616 } 617 618 /** 619 * @since 3.2 620 */ 621 public boolean isBestValue() { 622 return metric.isOptimizedBestValue() == Boolean.TRUE 623 && metric.getBestValue() != null 624 && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0) 625 && allNull(id, alertStatus, description, tendency, url, data) 626 && isZeroVariation(variation1, variation2, variation3, variation4, variation5); 627 } 628 629 private static boolean isZeroVariation(Double... variations) { 630 for (Double variation : variations) { 631 if (!((variation == null) || NumberUtils.compare(variation.doubleValue(), 0.0) == 0)) { 632 return false; 633 } 634 } 635 return true; 636 } 637 638 private static boolean allNull(Object... values) { 639 for (Object value : values) { 640 if (null != value) { 641 return false; 642 } 643 } 644 return true; 645 } 646 647 @Override 648 public boolean equals(Object o) { 649 if (this == o) { 650 return true; 651 } 652 if (o == null || getClass() != o.getClass()) { 653 return false; 654 } 655 656 Measure measure = (Measure) o; 657 if (metricKey != null ? !metricKey.equals(measure.metricKey) : measure.metricKey != null) { 658 return false; 659 } 660 if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) { 661 return false; 662 } 663 if (personId != null ? !personId.equals(measure.personId) : measure.personId != null) { 664 return false; 665 } 666 return true; 667 } 668 669 @Override 670 public int hashCode() { 671 int result = metricKey != null ? metricKey.hashCode() : 0; 672 result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); 673 result = 31 * result + (personId != null ? personId.hashCode() : 0); 674 return result; 675 } 676 677 @Override 678 public String toString() { 679 return ReflectionToStringBuilder.toString(this); 680 } 681}