001/*
002 * SonarQube
003 * Copyright (C) 2009-2016 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * This program 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 * This program 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 */
020package org.sonar.api.measures;
021
022import com.google.common.annotations.Beta;
023import java.io.Serializable;
024import java.util.Date;
025import javax.annotation.CheckForNull;
026import javax.annotation.Nullable;
027import org.apache.commons.lang.builder.ReflectionToStringBuilder;
028import org.apache.commons.lang.math.NumberUtils;
029import org.sonar.api.batch.sensor.SensorContext;
030
031/**
032 * A class to handle measures.
033 *
034 * @since 1.10
035 * @deprecated since 5.6. To create a new measure on scanner side use {@link SensorContext#newMeasure()}
036 */
037@Deprecated
038public class Measure<G extends Serializable> implements Serializable {
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   * @deprecated in 5.3. Decimal scale is provided by metric, not by measure.
046   */
047  @Deprecated
048  public static final int DEFAULT_PRECISION = 1;
049
050  protected String metricKey;
051  protected Metric<G> metric;
052  protected Double value;
053  protected String data;
054  protected String description;
055  protected Metric.Level alertStatus;
056  protected String alertText;
057  protected Date date;
058  protected Double variation1;
059  protected Double variation2;
060  protected Double variation3;
061  protected Double variation4;
062  protected Double variation5;
063  protected String url;
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   * 
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   * 
170   *
171   * @param mode the mode
172   * @return the measure object instance
173   */
174  public Measure<G> 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<G> 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<G> setMetric(Metric<G> 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<G> 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  @CheckForNull
242  public Double getValue() {
243    return value;
244  }
245
246  /**
247   * For internal use.
248   */
249  public G value() {
250    switch (getMetric().getType()) {
251      case BOOL:
252        return value == null ? null : (G) Boolean.valueOf(Double.doubleToRawLongBits(value) != 0L);
253      case INT:
254      case MILLISEC:
255      case RATING:
256        return value == null ? null : (G) Integer.valueOf(value.intValue());
257      case FLOAT:
258      case PERCENT:
259        return value == null ? null : (G) value;
260      case STRING:
261      case LEVEL:
262      case DATA:
263      case DISTRIB:
264        return data == null ? null : (G) data;
265      case WORK_DUR:
266        return value == null ? null : (G) Long.valueOf(value.longValue());
267      default:
268        if (getMetric().isNumericType()) {
269          return value == null ? null : (G) value;
270        } else if (getMetric().isDataType()) {
271          return data == null ? null : (G) data;
272        } else {
273          throw new UnsupportedOperationException("Unsupported type :" + getMetric().getType());
274        }
275    }
276  }
277
278  /**
279   * @return the value of the measure as an int
280   */
281  public Integer getIntValue() {
282    if (value == null) {
283      return null;
284    }
285    return value.intValue();
286  }
287
288  /**
289   * Sets the measure value with the default precision of 1
290   *
291   * @param v the measure value
292   * @return the measure object instance
293   */
294  public Measure<G> setValue(@Nullable Double v) {
295    return setValue(v, DEFAULT_PRECISION);
296  }
297
298  /**
299   * For internal use
300   */
301  public Measure<G> setRawValue(@Nullable Double v) {
302    this.value = v;
303    return this;
304  }
305
306  /**
307   * Sets the measure value as an int
308   *
309   * @param i the value
310   * @return the measure object instance
311   */
312  public Measure<G> setIntValue(Integer i) {
313    if (i == null) {
314      this.value = null;
315    } else {
316      this.value = Double.valueOf(i);
317    }
318    return this;
319  }
320
321  /**
322   * Sets the measure value with a given precision
323   *
324   * @return {@code this}
325   * @deprecated in 5.3. The decimal scale is given by the metric, not by the measure. Anyway this parameter was enforced to 1 before version 5.3.
326   */
327  @Deprecated
328  public Measure<G> setValue(@Nullable Double v, int decimalScale) {
329    if (v != null) {
330      if (Double.isNaN(v)) {
331        throw new IllegalArgumentException("Measure value can not be NaN");
332      }
333      this.value = v;
334    } else {
335      this.value = null;
336    }
337    return this;
338  }
339
340  /**
341   * @return the data field of the measure
342   */
343  @CheckForNull
344  public String getData() {
345    return data;
346  }
347
348  /**
349   * Sets the data field of the measure.
350   *
351   * @param s the data
352   * @return the measure object instance
353   */
354  public Measure<G> setData(String s) {
355    this.data = s;
356    return this;
357  }
358
359  /**
360   * Sets an alert level as the data field
361   *
362   * @param level the alert level
363   * @return the measure object instance
364   */
365  public Measure<G> setData(Metric.Level level) {
366    if (level == null) {
367      this.data = null;
368    } else {
369      this.data = level.toString();
370    }
371    return this;
372  }
373
374  /**
375   * @since 2.7
376   */
377  public Measure<G> unsetData() {
378    this.data = null;
379    return this;
380  }
381
382  /**
383   * @return the description of the measure
384   */
385  public String getDescription() {
386    return description;
387  }
388
389  /**
390   * Sets the measure description
391   *
392   * @param description the description
393   * @return the measure object instance
394   */
395  public Measure<G> setDescription(String description) {
396    this.description = description;
397    return this;
398  }
399
400  /**
401   * @return the alert status of the measure
402   */
403  public Metric.Level getAlertStatus() {
404    return alertStatus;
405  }
406
407  /**
408   * Set the alert status of the measure
409   *
410   * @param status the status
411   * @return the measure object instance
412   */
413  public Measure<G> setAlertStatus(@Nullable Metric.Level status) {
414    this.alertStatus = status;
415    return this;
416  }
417
418  /**
419   * @return the text associated to the alert on the measure
420   */
421  public String getAlertText() {
422    return alertText;
423  }
424
425  /**
426   * Sets the text associated to the alert on the measure
427   *
428   * @param alertText the text
429   * @return the measure object instance
430   */
431  public Measure<G> setAlertText(@Nullable String alertText) {
432    this.alertText = alertText;
433    return this;
434  }
435
436  /**
437   * Concept of measure trend is dropped.
438   * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392
439   * @return {@code null} since version 5.2
440   */
441  @Deprecated
442  @CheckForNull
443  public Integer getTendency() {
444    return null;
445  }
446
447  /**
448   * Concept of measure trend is dropped. This method does nothing.
449   * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392
450   * @return the measure object instance
451   */
452  @Deprecated
453  public Measure<G> setTendency(@Nullable Integer tendency) {
454    return this;
455  }
456
457  /**
458   * @return the first variation value
459   * @since 2.5
460   */
461  public Double getVariation1() {
462    return variation1;
463  }
464
465  /**
466   * Internal use only
467   *
468   * @since 2.5
469   */
470  public Measure<G> setVariation1(@Nullable Double d) {
471    this.variation1 = d;
472    return this;
473  }
474
475  /**
476   * @return the second variation value
477   * @since 2.5
478   */
479  public Double getVariation2() {
480    return variation2;
481  }
482
483  /**
484   * Internal use only
485   *
486   * @since 2.5
487   */
488  public Measure<G> setVariation2(@Nullable Double d) {
489    this.variation2 = d;
490    return this;
491  }
492
493  /**
494   * @return the third variation value
495   * @since 2.5
496   */
497  public Double getVariation3() {
498    return variation3;
499  }
500
501  /**
502   * Internal use only
503   *
504   * @since 2.5
505   */
506  public Measure<G> setVariation3(@Nullable Double d) {
507    this.variation3 = d;
508    return this;
509  }
510
511  /**
512   * @return the third variation value
513   * @since 2.5
514   */
515  public Double getVariation4() {
516    return variation4;
517  }
518
519  /**
520   * Internal use only
521   *
522   * @since 2.5
523   */
524  public Measure<G> setVariation4(@Nullable Double d) {
525    this.variation4 = d;
526    return this;
527  }
528
529  /**
530   * @return the third variation value
531   * @since 2.5
532   */
533  public Double getVariation5() {
534    return variation5;
535  }
536
537  /**
538   * Internal use only
539   *
540   * @since 2.5
541   */
542  public Measure<G> setVariation5(@Nullable Double d) {
543    this.variation5 = d;
544    return this;
545  }
546
547  /**
548   * @since 2.5
549   */
550  public Double getVariation(int index) {
551    switch (index) {
552      case 1:
553        return variation1;
554      case 2:
555        return variation2;
556      case 3:
557        return variation3;
558      case 4:
559        return variation4;
560      case 5:
561        return variation5;
562      default:
563        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
564    }
565  }
566
567  /**
568   * Internal use only
569   *
570   * @since 2.5
571   */
572  public Measure<G> setVariation(int index, Double d) {
573    switch (index) {
574      case 1:
575        variation1 = d;
576        break;
577      case 2:
578        variation2 = d;
579        break;
580      case 3:
581        variation3 = d;
582        break;
583      case 4:
584        variation4 = d;
585        break;
586      case 5:
587        variation5 = d;
588        break;
589      default:
590        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
591    }
592    return this;
593  }
594
595  /**
596   * @return the url of the measure
597   */
598  public String getUrl() {
599    return url;
600  }
601
602  /**
603   * Sets the URL of the measure
604   *
605   * @param url the url
606   * @return the measure object instance
607   */
608  public Measure<G> setUrl(String url) {
609    this.url = url;
610    return this;
611  }
612
613  /**
614   * @since 2.14
615   */
616  @CheckForNull
617  @Beta
618  public Integer getPersonId() {
619    return personId;
620  }
621
622  /**
623   * @since 2.14
624   */
625  @Beta
626  public Measure<G> setPersonId(@Nullable Integer i) {
627    this.personId = i;
628    return this;
629  }
630
631  /**
632   * @since 3.2
633   */
634  public boolean isBestValue() {
635    Double bestValue = metric.getBestValue();
636    return metric.isOptimizedBestValue() == Boolean.TRUE
637      && bestValue != null
638      && (value == null || NumberUtils.compare(bestValue, value) == 0)
639      && allNull(alertStatus, description, url, data)
640      && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
641  }
642
643  /**
644   * For internal use
645   */
646  public boolean isFromCore() {
647    return fromCore;
648  }
649
650  /**
651   * For internal use
652   */
653  public void setFromCore(boolean fromCore) {
654    this.fromCore = fromCore;
655  }
656
657  private static boolean isZeroVariation(Double... variations) {
658    for (Double variation : variations) {
659      if (variation != null && NumberUtils.compare(variation, 0.0) != 0) {
660        return false;
661      }
662    }
663    return true;
664  }
665
666  private static boolean allNull(Object... values) {
667    for (Object value : values) {
668      if (null != value) {
669        return false;
670      }
671    }
672    return true;
673  }
674
675  @Override
676  public boolean equals(Object o) {
677    if (this == o) {
678      return true;
679    }
680    if (o == null || getClass() != o.getClass()) {
681      return false;
682    }
683
684    Measure measure = (Measure) o;
685    if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) {
686      return false;
687    }
688    return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null));
689  }
690
691  @Override
692  public int hashCode() {
693    int result = metricKey != null ? metricKey.hashCode() : 0;
694    result = 31 * result + (personId != null ? personId.hashCode() : 0);
695    return result;
696  }
697
698  @Override
699  public String toString() {
700    return ReflectionToStringBuilder.toString(this);
701  }
702
703}