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