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