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