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