001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2013 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.qualitymodel.Characteristic;
026    
027    import java.math.BigDecimal;
028    import java.math.RoundingMode;
029    import java.util.Date;
030    
031    /**
032     * A class to handle measures.
033     *
034     * @since 1.10
035     */
036    public class Measure {
037      protected static final int MAX_TEXT_SIZE = 96;
038    
039      /**
040       * Default precision when saving a float type metric
041       */
042      public static final int DEFAULT_PRECISION = 1;
043    
044      private Long id; // for internal use
045      protected String metricKey;
046      protected Metric metric;
047      protected Double value;
048      protected String data;
049      protected String description;
050      protected Metric.Level alertStatus;
051      protected String alertText;
052      protected Integer tendency;
053      protected Date date;
054      protected Double variation1, variation2, variation3, variation4, variation5;
055      protected String url;
056      protected Characteristic characteristic;
057      protected Integer personId;
058      protected PersistenceMode persistenceMode = PersistenceMode.FULL;
059    
060      public Measure(String metricKey) {
061        this.metricKey = metricKey;
062      }
063    
064      /**
065       * Creates a measure with a metric
066       *
067       * @param metric the metric
068       */
069      public Measure(Metric metric) {
070        this.metric = metric;
071        this.metricKey = metric.getKey();
072      }
073    
074      /**
075       * Creates a measure with a metric and a value
076       *
077       * @param metric the metric
078       * @param value  its value
079       */
080      public Measure(Metric metric, Double value) {
081        this.metric = metric;
082        this.metricKey = metric.getKey();
083        setValue(value);
084      }
085    
086      /**
087       * Creates a measure with a metric, a value and a precision for the value
088       *
089       * @param metric    the metric
090       * @param value     its value
091       * @param precision the value precision
092       */
093      public Measure(Metric metric, Double value, int precision) {
094        this.metric = metric;
095        this.metricKey = metric.getKey();
096        setValue(value, precision);
097      }
098    
099      /**
100       * Creates a measure with a metric, a value and a data field
101       *
102       * @param metric the metric
103       * @param value  the value
104       * @param data   the data field
105       */
106      public Measure(Metric metric, Double value, String data) {
107        this.metric = metric;
108        this.metricKey = metric.getKey();
109        setValue(value);
110        setData(data);
111      }
112    
113      /**
114       * * Creates a measure with a metric and a data field
115       *
116       * @param metric the metric
117       * @param data   the data field
118       */
119      public Measure(Metric metric, String data) {
120        this.metric = metric;
121        this.metricKey = metric.getKey();
122        setData(data);
123      }
124    
125      /**
126       * Creates a measure with a metric and an alert level
127       *
128       * @param metric the metric
129       * @param level  the alert level
130       */
131      public Measure(Metric metric, Metric.Level level) {
132        this.metric = metric;
133        this.metricKey = metric.getKey();
134        if (level != null) {
135          this.data = level.toString();
136        }
137      }
138    
139      /**
140       * Creates an empty measure
141       */
142      public Measure() {
143      }
144    
145      /**
146       * Gets the persistence mode of the measure. Default persistence mode is FULL, except when instantiating the measure with a String
147       * parameter.
148       */
149      public PersistenceMode getPersistenceMode() {
150        return persistenceMode;
151      }
152    
153      /**
154       * <p>
155       * Sets the persistence mode of a measure.
156       * </p>
157       * <p>
158       * <b>WARNING : </b>Being able to reuse measures saved in memory is only possible within the same tree. In a multi-module project for
159       * example, a measure save in memory at the module level will not be accessible by the root project. In that case, database should be
160       * used.
161       * </p>
162       *
163       * @param mode the mode
164       * @return the measure object instance
165       */
166      public Measure setPersistenceMode(PersistenceMode mode) {
167        this.persistenceMode = mode;
168        return this;
169      }
170    
171      /**
172       * @return return the measures underlying metric
173       */
174      public Metric getMetric() {
175        return metric;
176      }
177    
178      public String getMetricKey() {
179        return metricKey;
180      }
181    
182      /**
183       * Set the underlying metric
184       *
185       * @param metric the metric
186       * @return the measure object instance
187       */
188      public Measure setMetric(Metric metric) {
189        this.metric = metric;
190        this.metricKey = metric.getKey();
191        return this;
192      }
193    
194      /**
195       * @return transforms and returns the data fields as a level of alert
196       */
197      public Metric.Level getDataAsLevel() {
198        if (data != null) {
199          return Metric.Level.valueOf(data);
200        }
201        return null;
202      }
203    
204      public boolean hasData() {
205        return data != null;
206      }
207    
208      /**
209       * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries
210       */
211      public Date getDate() {
212        return date;
213      }
214    
215      /**
216       * Sets the date of the measure - Used only in TimeMachine queries
217       *
218       * @param date the date
219       * @return the measure object instance
220       */
221      public Measure setDate(Date date) {
222        this.date = date;
223        return this;
224      }
225    
226      /**
227       * @return the value of the measure as a double
228       */
229      public Double getValue() {
230        return value;
231      }
232    
233      /**
234       * @return the value of the measure as an int
235       */
236      public Integer getIntValue() {
237        if (value == null) {
238          return null;
239        }
240        return value.intValue();
241      }
242    
243      /**
244       * Sets the measure value with the default precision of 1
245       *
246       * @param v the measure value
247       * @return the measure object instance
248       */
249      public Measure setValue(Double v) {
250        return setValue(v, DEFAULT_PRECISION);
251      }
252    
253      /**
254       * Sets the measure value as an int
255       *
256       * @param i the value
257       * @return the measure object instance
258       */
259      public Measure setIntValue(Integer i) {
260        if (i == null) {
261          this.value = null;
262        } else {
263          this.value = Double.valueOf(i);
264        }
265        return this;
266      }
267    
268      /**
269       * Sets the measure value with a given precision
270       *
271       * @param v         the measure value
272       * @param precision the measure value precision
273       * @return the measure object instance
274       */
275      public Measure setValue(Double v, int precision) {
276        if (v != null) {
277          if (Double.isNaN(v)) {
278            throw new IllegalArgumentException("Measure value can not be NaN");
279          }
280          this.value = scaleValue(v, precision);
281        } else {
282          this.value = null;
283        }
284        return this;
285      }
286    
287      private double scaleValue(double value, int scale) {
288        BigDecimal bd = BigDecimal.valueOf(value);
289        return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue();
290      }
291    
292      /**
293       * @return the data field of the measure
294       */
295      public String getData() {
296        return data;
297      }
298    
299      /**
300       * Sets the data field of the measure.
301       *
302       * @param s the data
303       * @return the measure object instance
304       */
305      public Measure setData(String s) {
306        this.data = s;
307        return this;
308      }
309    
310      /**
311       * Sets an alert level as the data field
312       *
313       * @param level the alert level
314       * @return the measure object instance
315       */
316      public Measure setData(Metric.Level level) {
317        if (level == null) {
318          this.data = null;
319        } else {
320          this.data = level.toString();
321        }
322        return this;
323      }
324    
325      /**
326       * @since 2.7
327       */
328      public Measure unsetData() {
329        this.data = null;
330        return this;
331      }
332    
333      /**
334       * @return the description of the measure
335       */
336      public String getDescription() {
337        return description;
338      }
339    
340      /**
341       * Sets the measure description
342       *
343       * @param description the description
344       * @return the measure object instance
345       */
346      public Measure setDescription(String description) {
347        this.description = description;
348        return this;
349      }
350    
351      /**
352       * @return the alert status of the measure
353       */
354      public Metric.Level getAlertStatus() {
355        return alertStatus;
356      }
357    
358      /**
359       * Set the alert status of the measure
360       *
361       * @param status the status
362       * @return the measure object instance
363       */
364      public Measure setAlertStatus(Metric.Level status) {
365        this.alertStatus = status;
366        return this;
367      }
368    
369      /**
370       * @return the text associated to the alert on the measure
371       */
372      public String getAlertText() {
373        return alertText;
374      }
375    
376      /**
377       * Sets the text associated to the alert on the measure
378       *
379       * @param alertText the text
380       * @return the measure object instance
381       */
382      public Measure setAlertText(String alertText) {
383        this.alertText = alertText;
384        return this;
385      }
386    
387      /**
388       * Gets the measure tendency
389       *
390       * @return the tendency
391       */
392      public Integer getTendency() {
393        return tendency;
394      }
395    
396      /**
397       * Sets the tendency for the measure - Internal use only
398       *
399       * @param tendency the tendency
400       * @return the measure object instance
401       */
402      public Measure setTendency(Integer tendency) {
403        this.tendency = tendency;
404        return this;
405      }
406    
407      /**
408       * @return the measure id - Internal use only
409       */
410      public Long getId() {
411        return id;
412      }
413    
414      /**
415       * Sets the measure id - Internal use only
416       *
417       * @param id the id
418       * @return the measure object instance
419       */
420      public Measure setId(Long id) {
421        this.id = id;
422        return this;
423      }
424    
425      /**
426       * @return the first variation value
427       * @since 2.5
428       */
429      public Double getVariation1() {
430        return variation1;
431      }
432    
433      /**
434       * Internal use only
435       *
436       * @since 2.5
437       */
438      public Measure setVariation1(Double d) {
439        this.variation1 = d;
440        return this;
441      }
442    
443      /**
444       * @return the second variation value
445       * @since 2.5
446       */
447      public Double getVariation2() {
448        return variation2;
449      }
450    
451      /**
452       * Internal use only
453       *
454       * @since 2.5
455       */
456      public Measure setVariation2(Double d) {
457        this.variation2 = d;
458        return this;
459      }
460    
461      /**
462       * @return the third variation value
463       * @since 2.5
464       */
465      public Double getVariation3() {
466        return variation3;
467      }
468    
469      /**
470       * Internal use only
471       *
472       * @since 2.5
473       */
474      public Measure setVariation3(Double d) {
475        this.variation3 = d;
476        return this;
477      }
478    
479      /**
480       * @return the third variation value
481       * @since 2.5
482       */
483      public Double getVariation4() {
484        return variation4;
485      }
486    
487      /**
488       * Internal use only
489       *
490       * @since 2.5
491       */
492      public Measure setVariation4(Double d) {
493        this.variation4 = d;
494        return this;
495      }
496    
497      /**
498       * @return the third variation value
499       * @since 2.5
500       */
501      public Double getVariation5() {
502        return variation5;
503      }
504    
505      /**
506       * Internal use only
507       *
508       * @since 2.5
509       */
510      public Measure setVariation5(Double d) {
511        this.variation5 = d;
512        return this;
513      }
514    
515      /**
516       * @since 2.5
517       */
518      public Double getVariation(int index) {
519        switch (index) {
520          case 1:
521            return variation1;
522          case 2:
523            return variation2;
524          case 3:
525            return variation3;
526          case 4:
527            return variation4;
528          case 5:
529            return variation5;
530          default:
531            throw new IndexOutOfBoundsException("Index should be in range from 1 to 5");
532        }
533      }
534    
535      /**
536       * Internal use only
537       *
538       * @since 2.5
539       */
540      public Measure setVariation(int index, Double d) {
541        switch (index) {
542          case 1:
543            variation1 = d;
544            break;
545          case 2:
546            variation2 = d;
547            break;
548          case 3:
549            variation3 = d;
550            break;
551          case 4:
552            variation4 = d;
553            break;
554          case 5:
555            variation5 = d;
556            break;
557          default:
558            throw new IndexOutOfBoundsException("Index should be in range from 1 to 5");
559        }
560        return this;
561      }
562    
563      /**
564       * @return the url of the measure
565       */
566      public String getUrl() {
567        return url;
568      }
569    
570      /**
571       * Sets the URL of the measure
572       *
573       * @param url the url
574       * @return the measure object instance
575       */
576      public Measure setUrl(String url) {
577        this.url = url;
578        return this;
579      }
580    
581      /**
582       * @since 2.3
583       */
584      public final Characteristic getCharacteristic() {
585        return characteristic;
586      }
587    
588      /**
589       * @since 2.3
590       */
591      public final Measure setCharacteristic(Characteristic characteristic) {
592        this.characteristic = characteristic;
593        return this;
594      }
595    
596      /**
597       * @since 2.14
598       */
599      @Beta
600      public Integer getPersonId() {
601        return personId;
602      }
603    
604      /**
605       * @since 2.14
606       */
607      @Beta
608      public Measure setPersonId(Integer i) {
609        this.personId = i;
610        return this;
611      }
612    
613      /**
614       * @since 3.2
615       */
616      public boolean isBestValue() {
617        return metric.isOptimizedBestValue() == Boolean.TRUE
618          && metric.getBestValue() != null
619          && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0)
620          && allNull(id, alertStatus, description, tendency, url, data)
621          && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
622      }
623    
624      private static boolean isZeroVariation(Double... variations) {
625        for (Double variation : variations) {
626          if (!((variation == null) || NumberUtils.compare(variation.doubleValue(), 0.0) == 0)) {
627            return false;
628          }
629        }
630        return true;
631      }
632    
633      private static boolean allNull(Object... values) {
634        for (Object value : values) {
635          if (null != value) {
636            return false;
637          }
638        }
639        return true;
640      }
641    
642      @Override
643      public boolean equals(Object o) {
644        if (this == o) {
645          return true;
646        }
647        if (o == null || getClass() != o.getClass()) {
648          return false;
649        }
650    
651        Measure measure = (Measure) o;
652        if (metricKey != null ? !metricKey.equals(measure.metricKey) : measure.metricKey != null) {
653          return false;
654        }
655        if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) {
656          return false;
657        }
658        if (personId != null ? !personId.equals(measure.personId) : measure.personId != null) {
659          return false;
660        }
661        return true;
662      }
663    
664      @Override
665      public int hashCode() {
666        int result = metricKey != null ? metricKey.hashCode() : 0;
667        result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
668        result = 31 * result + (personId != null ? personId.hashCode() : 0);
669        return result;
670      }
671    
672      @Override
673      public String toString() {
674        return ReflectionToStringBuilder.toString(this);
675      }
676    }