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