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   * Called by views when cloning measures
459   * @deprecated since 4.4 not used
460   */
461  @Deprecated
462  public Measure<G> setId(Long id) {
463    return this;
464  }
465
466  /**
467   * @return the first variation value
468   * @since 2.5
469   */
470  public Double getVariation1() {
471    return variation1;
472  }
473
474  /**
475   * Internal use only
476   *
477   * @since 2.5
478   */
479  public Measure<G> setVariation1(@Nullable Double d) {
480    this.variation1 = d;
481    return this;
482  }
483
484  /**
485   * @return the second variation value
486   * @since 2.5
487   */
488  public Double getVariation2() {
489    return variation2;
490  }
491
492  /**
493   * Internal use only
494   *
495   * @since 2.5
496   */
497  public Measure<G> setVariation2(@Nullable Double d) {
498    this.variation2 = d;
499    return this;
500  }
501
502  /**
503   * @return the third variation value
504   * @since 2.5
505   */
506  public Double getVariation3() {
507    return variation3;
508  }
509
510  /**
511   * Internal use only
512   *
513   * @since 2.5
514   */
515  public Measure<G> setVariation3(@Nullable Double d) {
516    this.variation3 = d;
517    return this;
518  }
519
520  /**
521   * @return the third variation value
522   * @since 2.5
523   */
524  public Double getVariation4() {
525    return variation4;
526  }
527
528  /**
529   * Internal use only
530   *
531   * @since 2.5
532   */
533  public Measure<G> setVariation4(@Nullable Double d) {
534    this.variation4 = d;
535    return this;
536  }
537
538  /**
539   * @return the third variation value
540   * @since 2.5
541   */
542  public Double getVariation5() {
543    return variation5;
544  }
545
546  /**
547   * Internal use only
548   *
549   * @since 2.5
550   */
551  public Measure<G> setVariation5(@Nullable Double d) {
552    this.variation5 = d;
553    return this;
554  }
555
556  /**
557   * @since 2.5
558   */
559  public Double getVariation(int index) {
560    switch (index) {
561      case 1:
562        return variation1;
563      case 2:
564        return variation2;
565      case 3:
566        return variation3;
567      case 4:
568        return variation4;
569      case 5:
570        return variation5;
571      default:
572        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
573    }
574  }
575
576  /**
577   * Internal use only
578   *
579   * @since 2.5
580   */
581  public Measure<G> setVariation(int index, Double d) {
582    switch (index) {
583      case 1:
584        variation1 = d;
585        break;
586      case 2:
587        variation2 = d;
588        break;
589      case 3:
590        variation3 = d;
591        break;
592      case 4:
593        variation4 = d;
594        break;
595      case 5:
596        variation5 = d;
597        break;
598      default:
599        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
600    }
601    return this;
602  }
603
604  /**
605   * @return the url of the measure
606   */
607  public String getUrl() {
608    return url;
609  }
610
611  /**
612   * Sets the URL of the measure
613   *
614   * @param url the url
615   * @return the measure object instance
616   */
617  public Measure<G> setUrl(String url) {
618    this.url = url;
619    return this;
620  }
621
622  /**
623   * @since 2.14
624   */
625  @CheckForNull
626  @Beta
627  public Integer getPersonId() {
628    return personId;
629  }
630
631  /**
632   * @since 2.14
633   */
634  @Beta
635  public Measure<G> setPersonId(@Nullable Integer i) {
636    this.personId = i;
637    return this;
638  }
639
640  /**
641   * @since 3.2
642   */
643  public boolean isBestValue() {
644    Double bestValue = metric.getBestValue();
645    return metric.isOptimizedBestValue() == Boolean.TRUE
646      && bestValue != null
647      && (value == null || NumberUtils.compare(bestValue, value) == 0)
648      && allNull(alertStatus, description, url, data)
649      && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
650  }
651
652  /**
653   * For internal use
654   */
655  public boolean isFromCore() {
656    return fromCore;
657  }
658
659  /**
660   * For internal use
661   */
662  public void setFromCore(boolean fromCore) {
663    this.fromCore = fromCore;
664  }
665
666  private static boolean isZeroVariation(Double... variations) {
667    for (Double variation : variations) {
668      if (variation != null && NumberUtils.compare(variation, 0.0) != 0) {
669        return false;
670      }
671    }
672    return true;
673  }
674
675  private static boolean allNull(Object... values) {
676    for (Object value : values) {
677      if (null != value) {
678        return false;
679      }
680    }
681    return true;
682  }
683
684  @Override
685  public boolean equals(Object o) {
686    if (this == o) {
687      return true;
688    }
689    if (o == null || getClass() != o.getClass()) {
690      return false;
691    }
692
693    Measure measure = (Measure) o;
694    if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) {
695      return false;
696    }
697    return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null));
698  }
699
700  @Override
701  public int hashCode() {
702    int result = metricKey != null ? metricKey.hashCode() : 0;
703    result = 31 * result + (personId != null ? personId.hashCode() : 0);
704    return result;
705  }
706
707  @Override
708  public String toString() {
709    return ReflectionToStringBuilder.toString(this);
710  }
711
712}