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