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    }