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