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