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