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