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