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