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;
029
030/**
031 * A class to handle measures.
032 *
033 * @since 1.10
034 */
035public class Measure<G extends Serializable> implements Serializable {
036  private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5";
037
038  protected static final int MAX_TEXT_SIZE = 96;
039
040  /**
041   * Default precision when saving a float type metric
042   * @deprecated in 5.3. Decimal scale is provided by metric, not by measure.
043   */
044  @Deprecated
045  public static final int DEFAULT_PRECISION = 1;
046
047  protected String metricKey;
048  protected Metric<G> metric;
049  protected Double value;
050  protected String data;
051  protected String description;
052  protected Metric.Level alertStatus;
053  protected String alertText;
054  protected Date date;
055  protected Double variation1;
056  protected Double variation2;
057  protected Double variation3;
058  protected Double variation4;
059  protected Double variation5;
060  protected String url;
061  protected Integer personId;
062  protected PersistenceMode persistenceMode = PersistenceMode.FULL;
063  private boolean fromCore;
064
065  public Measure(String metricKey) {
066    this.metricKey = metricKey;
067  }
068
069  /**
070   * Creates a measure with a metric
071   *
072   * @param metric the metric
073   */
074  public Measure(Metric metric) {
075    this.metric = metric;
076    this.metricKey = metric.getKey();
077  }
078
079  /**
080   * Creates a measure with a metric and a value
081   *
082   * @param metric the metric
083   * @param value  its value
084   */
085  public Measure(Metric metric, Double value) {
086    this.metric = metric;
087    this.metricKey = metric.getKey();
088    setValue(value);
089  }
090
091  /**
092   * Creates a measure with a metric, a value and a precision for the value
093   *
094   * @param metric    the metric
095   * @param value     its value
096   * @param precision the value precision
097   */
098  public Measure(Metric metric, Double value, int precision) {
099    this.metric = metric;
100    this.metricKey = metric.getKey();
101    setValue(value, precision);
102  }
103
104  /**
105   * Creates a measure with a metric, a value and a data field
106   *
107   * @param metric the metric
108   * @param value  the value
109   * @param data   the data field
110   */
111  public Measure(Metric metric, Double value, String data) {
112    this.metric = metric;
113    this.metricKey = metric.getKey();
114    setValue(value);
115    setData(data);
116  }
117
118  /**
119   * * Creates a measure with a metric and a data field
120   *
121   * @param metric the metric
122   * @param data   the data field
123   */
124  public Measure(Metric metric, String data) {
125    this.metric = metric;
126    this.metricKey = metric.getKey();
127    setData(data);
128  }
129
130  /**
131   * Creates a measure with a metric and an alert level
132   *
133   * @param metric the metric
134   * @param level  the alert level
135   */
136  public Measure(Metric metric, Metric.Level level) {
137    this.metric = metric;
138    this.metricKey = metric.getKey();
139    if (level != null) {
140      this.data = level.toString();
141    }
142  }
143
144  /**
145   * Creates an empty measure
146   */
147  public Measure() {
148  }
149
150  /**
151   * Gets the persistence mode of the measure. Default persistence mode is FULL, except when instantiating the measure with a String
152   * parameter.
153   */
154  public PersistenceMode getPersistenceMode() {
155    return persistenceMode;
156  }
157
158  /**
159   * <p>
160   * Sets the persistence mode of a measure.
161   * 
162   * <p>
163   * <b>WARNING : </b>Being able to reuse measures saved in memory is only possible within the same tree. In a multi-module project for
164   * example, a measure save in memory at the module level will not be accessible by the root project. In that case, database should be
165   * used.
166   * 
167   *
168   * @param mode the mode
169   * @return the measure object instance
170   */
171  public Measure<G> setPersistenceMode(@Nullable PersistenceMode mode) {
172    if (mode == null) {
173      this.persistenceMode = PersistenceMode.FULL;
174    } else {
175      this.persistenceMode = mode;
176    }
177    return this;
178  }
179
180  /**
181   * @return return the measures underlying metric
182   */
183  public Metric<G> getMetric() {
184    return metric;
185  }
186
187  public String getMetricKey() {
188    return metricKey;
189  }
190
191  /**
192   * Set the underlying metric
193   *
194   * @param metric the metric
195   * @return the measure object instance
196   */
197  public Measure<G> setMetric(Metric<G> metric) {
198    this.metric = metric;
199    this.metricKey = metric.getKey();
200    return this;
201  }
202
203  /**
204   * @return transforms and returns the data fields as a level of alert
205   */
206  public Metric.Level getDataAsLevel() {
207    if (data != null) {
208      return Metric.Level.valueOf(data);
209    }
210    return null;
211  }
212
213  public boolean hasData() {
214    return data != null;
215  }
216
217  /**
218   * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries
219   */
220  public Date getDate() {
221    return date;
222  }
223
224  /**
225   * Sets the date of the measure - Used only in TimeMachine queries
226   *
227   * @param date the date
228   * @return the measure object instance
229   */
230  public Measure<G> setDate(Date date) {
231    this.date = date;
232    return this;
233  }
234
235  /**
236   * @return the value of the measure as a double
237   */
238  @CheckForNull
239  public Double getValue() {
240    return value;
241  }
242
243  /**
244   * For internal use.
245   */
246  public G value() {
247    switch (getMetric().getType()) {
248      case BOOL:
249        return value == null ? null : (G) Boolean.valueOf(Double.doubleToRawLongBits(value) != 0L);
250      case INT:
251      case MILLISEC:
252      case RATING:
253        return value == null ? null : (G) Integer.valueOf(value.intValue());
254      case FLOAT:
255      case PERCENT:
256        return value == null ? null : (G) value;
257      case STRING:
258      case LEVEL:
259      case DATA:
260      case DISTRIB:
261        return data == null ? null : (G) data;
262      case WORK_DUR:
263        return value == null ? null : (G) Long.valueOf(value.longValue());
264      default:
265        if (getMetric().isNumericType()) {
266          return value == null ? null : (G) value;
267        } else if (getMetric().isDataType()) {
268          return data == null ? null : (G) data;
269        } else {
270          throw new UnsupportedOperationException("Unsupported type :" + getMetric().getType());
271        }
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(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(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   * Called by views when cloning measures
456   * @deprecated since 4.4 not used
457   */
458  @Deprecated
459  public Measure<G> setId(Long id) {
460    return this;
461  }
462
463  /**
464   * @return the first variation value
465   * @since 2.5
466   */
467  public Double getVariation1() {
468    return variation1;
469  }
470
471  /**
472   * Internal use only
473   *
474   * @since 2.5
475   */
476  public Measure<G> setVariation1(@Nullable Double d) {
477    this.variation1 = d;
478    return this;
479  }
480
481  /**
482   * @return the second variation value
483   * @since 2.5
484   */
485  public Double getVariation2() {
486    return variation2;
487  }
488
489  /**
490   * Internal use only
491   *
492   * @since 2.5
493   */
494  public Measure<G> setVariation2(@Nullable Double d) {
495    this.variation2 = d;
496    return this;
497  }
498
499  /**
500   * @return the third variation value
501   * @since 2.5
502   */
503  public Double getVariation3() {
504    return variation3;
505  }
506
507  /**
508   * Internal use only
509   *
510   * @since 2.5
511   */
512  public Measure<G> setVariation3(@Nullable Double d) {
513    this.variation3 = d;
514    return this;
515  }
516
517  /**
518   * @return the third variation value
519   * @since 2.5
520   */
521  public Double getVariation4() {
522    return variation4;
523  }
524
525  /**
526   * Internal use only
527   *
528   * @since 2.5
529   */
530  public Measure<G> setVariation4(@Nullable Double d) {
531    this.variation4 = d;
532    return this;
533  }
534
535  /**
536   * @return the third variation value
537   * @since 2.5
538   */
539  public Double getVariation5() {
540    return variation5;
541  }
542
543  /**
544   * Internal use only
545   *
546   * @since 2.5
547   */
548  public Measure<G> setVariation5(@Nullable Double d) {
549    this.variation5 = d;
550    return this;
551  }
552
553  /**
554   * @since 2.5
555   */
556  public Double getVariation(int index) {
557    switch (index) {
558      case 1:
559        return variation1;
560      case 2:
561        return variation2;
562      case 3:
563        return variation3;
564      case 4:
565        return variation4;
566      case 5:
567        return variation5;
568      default:
569        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
570    }
571  }
572
573  /**
574   * Internal use only
575   *
576   * @since 2.5
577   */
578  public Measure<G> setVariation(int index, Double d) {
579    switch (index) {
580      case 1:
581        variation1 = d;
582        break;
583      case 2:
584        variation2 = d;
585        break;
586      case 3:
587        variation3 = d;
588        break;
589      case 4:
590        variation4 = d;
591        break;
592      case 5:
593        variation5 = d;
594        break;
595      default:
596        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
597    }
598    return this;
599  }
600
601  /**
602   * @return the url of the measure
603   */
604  public String getUrl() {
605    return url;
606  }
607
608  /**
609   * Sets the URL of the measure
610   *
611   * @param url the url
612   * @return the measure object instance
613   */
614  public Measure<G> setUrl(String url) {
615    this.url = url;
616    return this;
617  }
618
619  /**
620   * @since 2.14
621   */
622  @CheckForNull
623  @Beta
624  public Integer getPersonId() {
625    return personId;
626  }
627
628  /**
629   * @since 2.14
630   */
631  @Beta
632  public Measure<G> setPersonId(@Nullable Integer i) {
633    this.personId = i;
634    return this;
635  }
636
637  /**
638   * @since 3.2
639   */
640  public boolean isBestValue() {
641    Double bestValue = metric.getBestValue();
642    return metric.isOptimizedBestValue() == Boolean.TRUE
643      && bestValue != null
644      && (value == null || NumberUtils.compare(bestValue, value) == 0)
645      && allNull(alertStatus, description, url, data)
646      && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
647  }
648
649  /**
650   * For internal use
651   */
652  public boolean isFromCore() {
653    return fromCore;
654  }
655
656  /**
657   * For internal use
658   */
659  public void setFromCore(boolean fromCore) {
660    this.fromCore = fromCore;
661  }
662
663  private static boolean isZeroVariation(Double... variations) {
664    for (Double variation : variations) {
665      if (variation != null && NumberUtils.compare(variation, 0.0) != 0) {
666        return false;
667      }
668    }
669    return true;
670  }
671
672  private static boolean allNull(Object... values) {
673    for (Object value : values) {
674      if (null != value) {
675        return false;
676      }
677    }
678    return true;
679  }
680
681  @Override
682  public boolean equals(Object o) {
683    if (this == o) {
684      return true;
685    }
686    if (o == null || getClass() != o.getClass()) {
687      return false;
688    }
689
690    Measure measure = (Measure) o;
691    if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) {
692      return false;
693    }
694    return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null));
695  }
696
697  @Override
698  public int hashCode() {
699    int result = metricKey != null ? metricKey.hashCode() : 0;
700    result = 31 * result + (personId != null ? personId.hashCode() : 0);
701    return result;
702  }
703
704  @Override
705  public String toString() {
706    return ReflectionToStringBuilder.toString(this);
707  }
708
709}