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    }