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