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    }