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