001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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.math.BigDecimal;
025import java.math.RoundingMode;
026import java.util.Date;
027import javax.annotation.CheckForNull;
028import javax.annotation.Nullable;
029import org.apache.commons.lang.builder.ReflectionToStringBuilder;
030import org.apache.commons.lang.math.NumberUtils;
031import org.sonar.api.technicaldebt.batch.Characteristic;
032import org.sonar.api.technicaldebt.batch.Requirement;
033
034/**
035 * A class to handle measures.
036 *
037 * @since 1.10
038 */
039public class Measure<G extends Serializable> implements Serializable {
040  private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5";
041
042  protected static final int MAX_TEXT_SIZE = 96;
043
044  /**
045   * Default precision when saving a float type metric
046   */
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   * @param v         the measure value
326   * @param precision the measure value precision
327   * @return the measure object instance
328   */
329  public Measure<G> setValue(@Nullable Double v, int precision) {
330    if (v != null) {
331      if (Double.isNaN(v)) {
332        throw new IllegalArgumentException("Measure value can not be NaN");
333      }
334      this.value = scaleValue(v, precision);
335    } else {
336      this.value = null;
337    }
338    return this;
339  }
340
341  private static double scaleValue(double value, int scale) {
342    BigDecimal bd = BigDecimal.valueOf(value);
343    return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue();
344  }
345
346  /**
347   * @return the data field of the measure
348   */
349  @CheckForNull
350  public String getData() {
351    return data;
352  }
353
354  /**
355   * Sets the data field of the measure.
356   *
357   * @param s the data
358   * @return the measure object instance
359   */
360  public Measure<G> setData(String s) {
361    this.data = s;
362    return this;
363  }
364
365  /**
366   * Sets an alert level as the data field
367   *
368   * @param level the alert level
369   * @return the measure object instance
370   */
371  public Measure<G> setData(Metric.Level level) {
372    if (level == null) {
373      this.data = null;
374    } else {
375      this.data = level.toString();
376    }
377    return this;
378  }
379
380  /**
381   * @since 2.7
382   */
383  public Measure<G> unsetData() {
384    this.data = null;
385    return this;
386  }
387
388  /**
389   * @return the description of the measure
390   */
391  public String getDescription() {
392    return description;
393  }
394
395  /**
396   * Sets the measure description
397   *
398   * @param description the description
399   * @return the measure object instance
400   */
401  public Measure<G> setDescription(String description) {
402    this.description = description;
403    return this;
404  }
405
406  /**
407   * @return the alert status of the measure
408   */
409  public Metric.Level getAlertStatus() {
410    return alertStatus;
411  }
412
413  /**
414   * Set the alert status of the measure
415   *
416   * @param status the status
417   * @return the measure object instance
418   */
419  public Measure<G> setAlertStatus(@Nullable Metric.Level status) {
420    this.alertStatus = status;
421    return this;
422  }
423
424  /**
425   * @return the text associated to the alert on the measure
426   */
427  public String getAlertText() {
428    return alertText;
429  }
430
431  /**
432   * Sets the text associated to the alert on the measure
433   *
434   * @param alertText the text
435   * @return the measure object instance
436   */
437  public Measure<G> setAlertText(@Nullable String alertText) {
438    this.alertText = alertText;
439    return this;
440  }
441
442  /**
443   * Concept of measure trend is dropped.
444   * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392
445   * @return {@code null} since version 5.2
446   */
447  @Deprecated
448  @CheckForNull
449  public Integer getTendency() {
450    return null;
451  }
452
453  /**
454   * Concept of measure trend is dropped. This method does nothing.
455   * @deprecated since 5.2. See https://jira.sonarsource.com/browse/SONAR-6392
456   * @return the measure object instance
457   */
458  @Deprecated
459  public Measure<G> setTendency(@Nullable Integer tendency) {
460    return this;
461  }
462
463  /**
464   * Called by views when cloning measures
465   * @deprecated since 4.4 not used
466   */
467  @Deprecated
468  public Measure<G> setId(Long id) {
469    return this;
470  }
471
472  /**
473   * @return the first variation value
474   * @since 2.5
475   */
476  public Double getVariation1() {
477    return variation1;
478  }
479
480  /**
481   * Internal use only
482   *
483   * @since 2.5
484   */
485  public Measure<G> setVariation1(@Nullable Double d) {
486    this.variation1 = d;
487    return this;
488  }
489
490  /**
491   * @return the second variation value
492   * @since 2.5
493   */
494  public Double getVariation2() {
495    return variation2;
496  }
497
498  /**
499   * Internal use only
500   *
501   * @since 2.5
502   */
503  public Measure<G> setVariation2(@Nullable Double d) {
504    this.variation2 = d;
505    return this;
506  }
507
508  /**
509   * @return the third variation value
510   * @since 2.5
511   */
512  public Double getVariation3() {
513    return variation3;
514  }
515
516  /**
517   * Internal use only
518   *
519   * @since 2.5
520   */
521  public Measure<G> setVariation3(@Nullable Double d) {
522    this.variation3 = d;
523    return this;
524  }
525
526  /**
527   * @return the third variation value
528   * @since 2.5
529   */
530  public Double getVariation4() {
531    return variation4;
532  }
533
534  /**
535   * Internal use only
536   *
537   * @since 2.5
538   */
539  public Measure<G> setVariation4(@Nullable Double d) {
540    this.variation4 = d;
541    return this;
542  }
543
544  /**
545   * @return the third variation value
546   * @since 2.5
547   */
548  public Double getVariation5() {
549    return variation5;
550  }
551
552  /**
553   * Internal use only
554   *
555   * @since 2.5
556   */
557  public Measure<G> setVariation5(@Nullable Double d) {
558    this.variation5 = d;
559    return this;
560  }
561
562  /**
563   * @since 2.5
564   */
565  public Double getVariation(int index) {
566    switch (index) {
567      case 1:
568        return variation1;
569      case 2:
570        return variation2;
571      case 3:
572        return variation3;
573      case 4:
574        return variation4;
575      case 5:
576        return variation5;
577      default:
578        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
579    }
580  }
581
582  /**
583   * Internal use only
584   *
585   * @since 2.5
586   */
587  public Measure<G> setVariation(int index, Double d) {
588    switch (index) {
589      case 1:
590        variation1 = d;
591        break;
592      case 2:
593        variation2 = d;
594        break;
595      case 3:
596        variation3 = d;
597        break;
598      case 4:
599        variation4 = d;
600        break;
601      case 5:
602        variation5 = d;
603        break;
604      default:
605        throw new IndexOutOfBoundsException(INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5);
606    }
607    return this;
608  }
609
610  /**
611   * @return the url of the measure
612   */
613  public String getUrl() {
614    return url;
615  }
616
617  /**
618   * Sets the URL of the measure
619   *
620   * @param url the url
621   * @return the measure object instance
622   */
623  public Measure<G> setUrl(String url) {
624    this.url = url;
625    return this;
626  }
627
628  /**
629   * @since 4.1
630   */
631  @CheckForNull
632  public final Characteristic getCharacteristic() {
633    return characteristic;
634  }
635
636  /**
637   * @since 4.1
638   */
639  public final Measure<G> setCharacteristic(@Nullable Characteristic characteristic) {
640    this.characteristic = characteristic;
641    return this;
642  }
643
644  /**
645   * @since 4.1
646   * @deprecated since 4.3.
647   */
648  @Deprecated
649  @CheckForNull
650  public final Requirement getRequirement() {
651    return requirement;
652  }
653
654  /**
655   * @since 4.1
656   * @deprecated since 4.3
657   */
658  @Deprecated
659  public final Measure<G> setRequirement(@Nullable Requirement requirement) {
660    this.requirement = requirement;
661    return this;
662  }
663
664  /**
665   * @since 2.14
666   */
667  @CheckForNull
668  @Beta
669  public Integer getPersonId() {
670    return personId;
671  }
672
673  /**
674   * @since 2.14
675   */
676  @Beta
677  public Measure<G> setPersonId(@Nullable Integer i) {
678    this.personId = i;
679    return this;
680  }
681
682  /**
683   * @since 3.2
684   */
685  public boolean isBestValue() {
686    Double bestValue = metric.getBestValue();
687    return metric.isOptimizedBestValue() == Boolean.TRUE
688      && bestValue != null
689      && (value == null || NumberUtils.compare(bestValue, value) == 0)
690      && allNull(alertStatus, description, url, data)
691      && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
692  }
693
694  /**
695   * For internal use
696   */
697  public boolean isFromCore() {
698    return fromCore;
699  }
700
701  /**
702   * For internal use
703   */
704  public void setFromCore(boolean fromCore) {
705    this.fromCore = fromCore;
706  }
707
708  private static boolean isZeroVariation(Double... variations) {
709    for (Double variation : variations) {
710      if (variation != null && NumberUtils.compare(variation, 0.0) != 0) {
711        return false;
712      }
713    }
714    return true;
715  }
716
717  private static boolean allNull(Object... values) {
718    for (Object value : values) {
719      if (null != value) {
720        return false;
721      }
722    }
723    return true;
724  }
725
726  @Override
727  public boolean equals(Object o) {
728    if (this == o) {
729      return true;
730    }
731    if (o == null || getClass() != o.getClass()) {
732      return false;
733    }
734
735    Measure measure = (Measure) o;
736    if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) {
737      return false;
738    }
739    if (characteristic != null ? !characteristic.equals(measure.characteristic) : (measure.characteristic != null)) {
740      return false;
741    }
742    return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null));
743  }
744
745  @Override
746  public int hashCode() {
747    int result = metricKey != null ? metricKey.hashCode() : 0;
748    result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
749    result = 31 * result + (personId != null ? personId.hashCode() : 0);
750    return result;
751  }
752
753  @Override
754  public String toString() {
755    return ReflectionToStringBuilder.toString(this);
756  }
757
758}