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 public String getData() {
347 return data;
348 }
349
350 /**
351 * Sets the data field of the measure.
352 *
353 * @param s the data
354 * @return the measure object instance
355 */
356 public Measure setData(String s) {
357 this.data = s;
358 return this;
359 }
360
361 /**
362 * Sets an alert level as the data field
363 *
364 * @param level the alert level
365 * @return the measure object instance
366 */
367 public Measure setData(Metric.Level level) {
368 if (level == null) {
369 this.data = null;
370 } else {
371 this.data = level.toString();
372 }
373 return this;
374 }
375
376 /**
377 * @since 2.7
378 */
379 public Measure unsetData() {
380 this.data = null;
381 return this;
382 }
383
384 /**
385 * @return the description of the measure
386 */
387 public String getDescription() {
388 return description;
389 }
390
391 /**
392 * Sets the measure description
393 *
394 * @param description the description
395 * @return the measure object instance
396 */
397 public Measure setDescription(String description) {
398 this.description = description;
399 return this;
400 }
401
402 /**
403 * @return the alert status of the measure
404 */
405 public Metric.Level getAlertStatus() {
406 return alertStatus;
407 }
408
409 /**
410 * Set the alert status of the measure
411 *
412 * @param status the status
413 * @return the measure object instance
414 */
415 public Measure setAlertStatus(@Nullable Metric.Level status) {
416 this.alertStatus = status;
417 return this;
418 }
419
420 /**
421 * @return the text associated to the alert on the measure
422 */
423 public String getAlertText() {
424 return alertText;
425 }
426
427 /**
428 * Sets the text associated to the alert on the measure
429 *
430 * @param alertText the text
431 * @return the measure object instance
432 */
433 public Measure setAlertText(@Nullable String alertText) {
434 this.alertText = alertText;
435 return this;
436 }
437
438 /**
439 * Gets the measure tendency
440 *
441 * @return the tendency
442 */
443 public Integer getTendency() {
444 return tendency;
445 }
446
447 /**
448 * Sets the tendency for the measure - Internal use only
449 *
450 * @param tendency the tendency
451 * @return the measure object instance
452 */
453 public Measure setTendency(@Nullable Integer tendency) {
454 this.tendency = tendency;
455 return this;
456 }
457
458 /**
459 * Called by views when cloning measures
460 * @deprecated since 4.4 not used
461 */
462 @Deprecated
463 public Measure setId(Long id) {
464 return this;
465 }
466
467 /**
468 * @return the first variation value
469 * @since 2.5
470 */
471 public Double getVariation1() {
472 return variation1;
473 }
474
475 /**
476 * Internal use only
477 *
478 * @since 2.5
479 */
480 public Measure setVariation1(@Nullable Double d) {
481 this.variation1 = d;
482 return this;
483 }
484
485 /**
486 * @return the second variation value
487 * @since 2.5
488 */
489 public Double getVariation2() {
490 return variation2;
491 }
492
493 /**
494 * Internal use only
495 *
496 * @since 2.5
497 */
498 public Measure setVariation2(@Nullable Double d) {
499 this.variation2 = d;
500 return this;
501 }
502
503 /**
504 * @return the third variation value
505 * @since 2.5
506 */
507 public Double getVariation3() {
508 return variation3;
509 }
510
511 /**
512 * Internal use only
513 *
514 * @since 2.5
515 */
516 public Measure setVariation3(@Nullable Double d) {
517 this.variation3 = d;
518 return this;
519 }
520
521 /**
522 * @return the third variation value
523 * @since 2.5
524 */
525 public Double getVariation4() {
526 return variation4;
527 }
528
529 /**
530 * Internal use only
531 *
532 * @since 2.5
533 */
534 public Measure setVariation4(@Nullable Double d) {
535 this.variation4 = d;
536 return this;
537 }
538
539 /**
540 * @return the third variation value
541 * @since 2.5
542 */
543 public Double getVariation5() {
544 return variation5;
545 }
546
547 /**
548 * Internal use only
549 *
550 * @since 2.5
551 */
552 public Measure setVariation5(@Nullable Double d) {
553 this.variation5 = d;
554 return this;
555 }
556
557 /**
558 * @since 2.5
559 */
560 public Double getVariation(int index) {
561 switch (index) {
562 case 1:
563 return variation1;
564 case 2:
565 return variation2;
566 case 3:
567 return variation3;
568 case 4:
569 return variation4;
570 case 5:
571 return variation5;
572 default:
573 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
574 }
575 }
576
577 /**
578 * Internal use only
579 *
580 * @since 2.5
581 */
582 public Measure setVariation(int index, Double d) {
583 switch (index) {
584 case 1:
585 variation1 = d;
586 break;
587 case 2:
588 variation2 = d;
589 break;
590 case 3:
591 variation3 = d;
592 break;
593 case 4:
594 variation4 = d;
595 break;
596 case 5:
597 variation5 = d;
598 break;
599 default:
600 throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
601 }
602 return this;
603 }
604
605 /**
606 * @return the url of the measure
607 */
608 public String getUrl() {
609 return url;
610 }
611
612 /**
613 * Sets the URL of the measure
614 *
615 * @param url the url
616 * @return the measure object instance
617 */
618 public Measure setUrl(String url) {
619 this.url = url;
620 return this;
621 }
622
623 /**
624 * @since 4.1
625 */
626 @CheckForNull
627 public final Characteristic getCharacteristic() {
628 return characteristic;
629 }
630
631 /**
632 * @since 4.1
633 */
634 public final Measure setCharacteristic(@Nullable Characteristic characteristic) {
635 this.characteristic = characteristic;
636 return this;
637 }
638
639 /**
640 * @since 4.1
641 * @deprecated since 4.3.
642 */
643 @Deprecated
644 @CheckForNull
645 public final Requirement getRequirement() {
646 return requirement;
647 }
648
649 /**
650 * @since 4.1
651 * @deprecated since 4.3
652 */
653 @Deprecated
654 public final Measure setRequirement(@Nullable Requirement requirement) {
655 this.requirement = requirement;
656 return this;
657 }
658
659 /**
660 * @since 2.14
661 */
662 @CheckForNull
663 @Beta
664 public Integer getPersonId() {
665 return personId;
666 }
667
668 /**
669 * @since 2.14
670 */
671 @Beta
672 public Measure setPersonId(@Nullable Integer i) {
673 this.personId = i;
674 return this;
675 }
676
677 /**
678 * @since 3.2
679 */
680 public boolean isBestValue() {
681 return metric.isOptimizedBestValue() == Boolean.TRUE
682 && metric.getBestValue() != null
683 && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0)
684 && allNull(alertStatus, description, tendency, url, data)
685 && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
686 }
687
688 private static boolean isZeroVariation(Double... variations) {
689 for (Double variation : variations) {
690 if (!((variation == null) || NumberUtils.compare(variation, 0.0) == 0)) {
691 return false;
692 }
693 }
694 return true;
695 }
696
697 private static boolean allNull(Object... values) {
698 for (Object value : values) {
699 if (null != value) {
700 return false;
701 }
702 }
703 return true;
704 }
705
706 @Override
707 public boolean equals(Object o) {
708 if (this == o) {
709 return true;
710 }
711 if (o == null || getClass() != o.getClass()) {
712 return false;
713 }
714
715 Measure measure = (Measure) o;
716 if (metricKey != null ? !metricKey.equals(measure.metricKey) : measure.metricKey != null) {
717 return false;
718 }
719 if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) {
720 return false;
721 }
722 if (personId != null ? !personId.equals(measure.personId) : measure.personId != null) {
723 return false;
724 }
725 return true;
726 }
727
728 @Override
729 public int hashCode() {
730 int result = metricKey != null ? metricKey.hashCode() : 0;
731 result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
732 result = 31 * result + (personId != null ? personId.hashCode() : 0);
733 return result;
734 }
735
736 @Override
737 public String toString() {
738 return ReflectionToStringBuilder.toString(this);
739 }
740
741 }