001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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     */
020    package org.sonar.api.measures;
021    
022    import org.apache.commons.lang.builder.ToStringBuilder;
023    import org.sonar.api.qualitymodel.Characteristic;
024    
025    import java.math.BigDecimal;
026    import java.math.RoundingMode;
027    import java.util.Date;
028    
029    /**
030     * A class to handle measures.
031     * <p/>
032     *
033     * @since 1.10
034     */
035    public class Measure {
036      protected static final int MAX_TEXT_SIZE = 96;
037    
038      /**
039       * Default precision when saving a float type metric
040       */
041      public final static int DEFAULT_PRECISION = 1;
042    
043      private Long id; // for internal use
044      protected Metric metric;
045      protected Double value;
046      protected String data;
047      protected String description;
048      protected Metric.Level alertStatus;
049      protected String alertText;
050      protected Integer tendency;
051      protected Date date;
052      protected Double diff1, diff2, diff3;
053      protected String url;
054      protected Characteristic characteristic;
055      protected PersistenceMode persistenceMode = PersistenceMode.FULL;
056    
057      /**
058       * Creates a measure with a metric
059       *
060       * @param metric the metric
061       */
062      public Measure(Metric metric) {
063        this.metric = metric;
064      }
065    
066      /**
067       * Creates a measure with a metric and a value
068       *
069       * @param metric the metric
070       * @param value  its value
071       */
072      public Measure(Metric metric, Double value) {
073        this.metric = metric;
074        setValue(value);
075      }
076    
077      /**
078       * Creates a measure with a metric, a value and a precision for the value
079       *
080       * @param metric    the metric
081       * @param value     its value
082       * @param precision the value precision
083       */
084      public Measure(Metric metric, Double value, int precision) {
085        this.metric = metric;
086        setValue(value, precision);
087      }
088    
089      /**
090       * Creates a measure with a metric, a value and a data field
091       *
092       * @param metric the metric
093       * @param value  the value
094       * @param data   the data field
095       */
096      public Measure(Metric metric, Double value, String data) {
097        this.metric = metric;
098        setValue(value);
099        setData(data);
100      }
101    
102      /**
103       * * Creates a measure with a metric and a data field
104       *
105       * @param metric the metric
106       * @param data   the data field
107       */
108      public Measure(Metric metric, String data) {
109        this.metric = metric;
110        setData(data);
111      }
112    
113      /**
114       * Creates a measure with a metric and an alert level
115       *
116       * @param metric the metric
117       * @param level  the alert level
118       */
119      public Measure(Metric metric, Metric.Level level) {
120        this.metric = metric;
121        if (level != null) {
122          this.data = level.toString();
123        }
124      }
125    
126      /**
127       * Creates an empty measure
128       */
129      public Measure() {
130      }
131    
132      /**
133       * Gets the persistence mode of the measure. Default persistence mode is FULL,
134       * except when instantiating the measure with a String parameter.
135       */
136      public PersistenceMode getPersistenceMode() {
137        return persistenceMode;
138      }
139    
140      /**
141       * <p>Sets the persistence mode of a measure.</p>
142       * <p><b>WARNING : </b>Being able to reuse measures saved in memory is only possible within the same tree.
143       * In a multi-module project for example, a measure save in memory at the module level will not be accessible by
144       * the root project. In that case, database should be used.
145       * </p>
146       *
147       * @param mode the mode
148       * @return the measure object instance
149       */
150      public Measure setPersistenceMode(PersistenceMode mode) {
151        this.persistenceMode = mode;
152        return this;
153      }
154    
155      /**
156       * @return return the measures underlying metric
157       */
158      public Metric getMetric() {
159        return metric;
160      }
161    
162      /**
163       * Set the underlying metric
164       *
165       * @param metric the metric
166       * @return the measure object instance
167       */
168      public Measure setMetric(Metric metric) {
169        this.metric = metric;
170        return this;
171      }
172    
173      /**
174       * @return transforms and returns the data fields as a level of alert
175       */
176      public Metric.Level getDataAsLevel() {
177        if (data != null) {
178          return Metric.Level.valueOf(data);
179        }
180        return null;
181      }
182    
183      /**
184       * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries
185       */
186      public Date getDate() {
187        return date;
188      }
189    
190      /**
191       * Sets the date of the measure - Used only in TimeMachine queries
192       *
193       * @param date the date
194       * @return the measure object instance
195       */
196      public Measure setDate(Date date) {
197        this.date = date;
198        return this;
199      }
200    
201      /**
202       * @return the value of the measure as a double
203       */
204      public Double getValue() {
205        return value;
206      }
207    
208      /**
209       * @return the value of the measure as an int
210       */
211      public Integer getIntValue() {
212        if (value == null) {
213          return null;
214        }
215        return value.intValue();
216      }
217    
218      /**
219       * Sets the measure value with the default precision of 1
220       *
221       * @param v the measure value
222       * @return the measure object instance
223       */
224      public Measure setValue(Double v) {
225        return setValue(v, DEFAULT_PRECISION);
226      }
227    
228      /**
229       * Sets the measure value as an int
230       *
231       * @param i the value
232       * @return the measure object instance
233       */
234      public Measure setIntValue(Integer i) {
235        if (i == null) {
236          this.value = null;
237        } else {
238          this.value = Double.valueOf(i);
239        }
240        return this;
241      }
242    
243      /**
244       * Sets the measure value with a given precision
245       *
246       * @param v         the measure value
247       * @param precision the measure value precision
248       * @return the measure object instance
249       */
250      public Measure setValue(Double v, int precision) {
251        if (v != null) {
252          if (Double.isNaN(v)) {
253            throw new IllegalArgumentException("Measure value can not be NaN");
254          }
255          this.value = scaleValue(v, precision);
256        } else {
257          this.value = null;
258        }
259        return this;
260      }
261    
262      private double scaleValue(double value, int scale) {
263        BigDecimal bd = BigDecimal.valueOf(value);
264        return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue();
265      }
266    
267      /**
268       * @return the data field of the measure
269       */
270      public String getData() {
271        return data;
272      }
273    
274      /**
275       * Sets the data field of the measure.
276       *
277       * @param s the data
278       * @return the measure object instance
279       */
280      public Measure setData(String s) {
281        if (s != null && s.length() >= MAX_TEXT_SIZE && !metric.isDataType()) {
282          throw new IllegalArgumentException("Data is too long for non-data metric : size=" + s.length() + ", max=" + MAX_TEXT_SIZE);
283        }
284        this.data = s;
285        return this;
286      }
287    
288      /**
289       * Sets an alert level as the data field
290       *
291       * @param level the alert level
292       * @return the measure object instance
293       */
294      public Measure setData(Metric.Level level) {
295        if (level == null) {
296          this.data = null;
297        } else {
298          this.data = level.toString();
299        }
300        return this;
301      }
302    
303      /**
304       * @return the description of the measure
305       */
306      public String getDescription() {
307        return description;
308      }
309    
310      /**
311       * Sets the measure description
312       *
313       * @param description the description
314       * @return the measure object instance
315       */
316      public Measure setDescription(String description) {
317        this.description = description;
318        return this;
319      }
320    
321      /**
322       * @return the alert status of the measure
323       */
324      public Metric.Level getAlertStatus() {
325        return alertStatus;
326      }
327    
328      /**
329       * Set the alert status of the measure
330       *
331       * @param status the status
332       * @return the measure object instance
333       */
334      public Measure setAlertStatus(Metric.Level status) {
335        this.alertStatus = status;
336        return this;
337      }
338    
339      /**
340       * @return the text associated to the alert on the measure
341       */
342      public String getAlertText() {
343        return alertText;
344      }
345    
346      /**
347       * Sets the text associated to the alert on the measure
348       *
349       * @param alertText the text
350       * @return the measure object instance
351       */
352      public Measure setAlertText(String alertText) {
353        this.alertText = alertText;
354        return this;
355      }
356    
357      /**
358       * Gets the measure tendency
359       *
360       * @return the tendency
361       */
362      public Integer getTendency() {
363        return tendency;
364      }
365    
366      /**
367       * Sets the tendency for the measure
368       *
369       * @param tendency the tendency
370       * @return the measure object instance
371       */
372      public Measure setTendency(Integer tendency) {
373        this.tendency = tendency;
374        return this;
375      }
376    
377      /**
378       * @return the measure id - Internal use only
379       */
380      public Long getId() {
381        return id;
382      }
383    
384      /**
385       * Sets the measure id - Internal use only
386       *
387       * @param id the id
388       * @return the measure object instance
389       */
390      public Measure setId(Long id) {
391        this.id = id;
392        return this;
393      }
394    
395      /**
396       * @return the first differential value of the measure
397       */
398      public Double getDiffValue1() {
399        return diff1;
400      }
401    
402      /**
403       * Sets the first differential value of the measure
404       *
405       * @param diff1 the diff
406       * @return the measure object instance
407       */
408      public Measure setDiffValue1(Double diff1) {
409        this.diff1 = diff1;
410        return this;
411      }
412    
413      /**
414       * @return the second differential value of the measure
415       */
416      public Double getDiffValue2() {
417        return diff2;
418      }
419    
420      /**
421       * Sets the second differential value of the measure
422       *
423       * @param diff2 the diff
424       * @return the measure object instance
425       */
426      public Measure setDiffValue2(Double diff2) {
427        this.diff2 = diff2;
428        return this;
429      }
430    
431      /**
432       * @return the third differential value of the measure
433       */
434      public Double getDiffValue3() {
435        return diff3;
436      }
437    
438      /**
439       * Sets the third differential value of the measure
440       *
441       * @param diff3 the diff
442       * @return the measure object instance
443       */
444      public Measure setDiffValue3(Double diff3) {
445        this.diff3 = diff3;
446        return this;
447      }
448    
449      /**
450       * @return the url of the measure
451       */
452      public String getUrl() {
453        return url;
454      }
455    
456      /**
457       * Sets the URL of the measure
458       *
459       * @param url the url
460       * @return the measure object instance
461       */
462      public Measure setUrl(String url) {
463        this.url = url;
464        return this;
465      }
466    
467      public final Characteristic getCharacteristic() {
468        return characteristic;
469      }
470    
471      public final Measure setCharacteristic(Characteristic characteristic) {
472        this.characteristic = characteristic;
473        return this;
474      }
475    
476    
477    //  @Override
478    //  public boolean equals(Object obj) {
479    //    if (!(obj.getClass().equals(Measure.class))) {
480    //      return false;
481    //    }
482    //    if (this == obj) {
483    //      return true;
484    //    }
485    //    Measure rhs = (Measure) obj;
486    //    return new EqualsBuilder()
487    //        .append(metric, rhs.getMetric())
488    //        .append(characteristic, rhs.getCharacteristic())
489    //        .isEquals();
490    //  }
491    //
492    //  @Override
493    //  public int hashCode() {
494    //    return (metric != null ? metric.hashCode() : 0);
495    //  }
496    //
497    
498    
499      @Override
500      public boolean equals(Object o) {
501        if (this == o) {
502          return true;
503        }
504        if (!(o.getClass().equals(Measure.class))) {
505          return false;
506        }
507    
508        Measure measure = (Measure) o;
509        if (metric != null ? !metric.equals(measure.metric) : measure.metric != null) {
510          return false;
511        }
512        if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) {
513          return false;
514        }
515        return true;
516      }
517    
518      @Override
519      public int hashCode() {
520        int result = metric != null ? metric.hashCode() : 0;
521        result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
522        return result;
523      }
524    
525      @Override
526      public String toString() {
527        return new ToStringBuilder(this).
528            append("id", id).
529            append("metric", metric).
530            append("value", value).
531            append("data", data).
532            append("description", description).
533            append("alertStatus", alertStatus).
534            append("alertText", alertText).
535            append("tendency", tendency).
536            append("characteristic", characteristic).
537            append("diff1", diff1).
538            append("diff2", diff2).
539            append("diff3", diff3).
540            toString();
541      }
542    
543      public boolean hasOptionalData() {
544        return getAlertStatus() != null ||
545            getAlertText() != null ||
546            getDescription() != null ||
547            getDiffValue1() != null ||
548            getDiffValue2() != null ||
549            getDiffValue3() != null ||
550            getData() != null ||
551            getTendency() != null ||
552            getUrl() != null;
553      }
554    }