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     */
020    package org.sonar.api.database.model;
021    
022    import com.google.common.base.Charsets;
023    import com.google.common.base.Throwables;
024    import org.apache.commons.lang.builder.ReflectionToStringBuilder;
025    import org.apache.commons.lang.builder.ToStringStyle;
026    import org.sonar.api.database.DatabaseSession;
027    import org.sonar.api.measures.Metric;
028    import org.sonar.api.rules.RulePriority;
029    
030    import javax.persistence.Column;
031    import javax.persistence.Entity;
032    import javax.persistence.EnumType;
033    import javax.persistence.Enumerated;
034    import javax.persistence.GeneratedValue;
035    import javax.persistence.Id;
036    import javax.persistence.Table;
037    import javax.persistence.Temporal;
038    import javax.persistence.TemporalType;
039    
040    import java.io.UnsupportedEncodingException;
041    import java.util.Date;
042    
043    /**
044     * This class is the Hibernate model to store a measure in the DB
045     */
046    @Entity
047    @Table(name = "project_measures")
048    public class MeasureModel implements Cloneable {
049    
050      public static final int TEXT_VALUE_LENGTH = 4000;
051    
052      @Id
053      @Column(name = "id")
054      @GeneratedValue
055      private Long id;
056    
057      @Column(name = "value", updatable = true, nullable = true, precision = 30, scale = 20)
058      private Double value = 0.0;
059    
060      @Column(name = "text_value", updatable = true, nullable = true, length = TEXT_VALUE_LENGTH)
061      private String textValue;
062    
063      @Column(name = "tendency", updatable = true, nullable = true)
064      private Integer tendency;
065    
066      @Column(name = "metric_id", updatable = false, nullable = false)
067      private Integer metricId;
068    
069      @Column(name = "snapshot_id", updatable = true, nullable = true)
070      private Integer snapshotId;
071    
072      @Column(name = "project_id", updatable = true, nullable = true)
073      private Integer projectId;
074    
075      @Column(name = "description", updatable = true, nullable = true, length = 4000)
076      private String description;
077    
078      @Temporal(TemporalType.TIMESTAMP)
079      @Column(name = "measure_date", updatable = true, nullable = true)
080      private Date measureDate;
081    
082      @Column(name = "rule_id", updatable = true, nullable = true)
083      private Integer ruleId;
084    
085      /**
086       * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
087       */
088      @Deprecated
089      @Column(name = "rules_category_id", nullable = true)
090      private Integer rulesCategoryId;// NOSONAR this field is kept for backward-compatiblity of API
091    
092      @Column(name = "rule_priority", updatable = false, nullable = true)
093      @Enumerated(EnumType.ORDINAL)
094      private RulePriority rulePriority;
095    
096      @Column(name = "alert_status", updatable = true, nullable = true, length = 5)
097      private String alertStatus;
098    
099      @Column(name = "alert_text", updatable = true, nullable = true, length = 4000)
100      private String alertText;
101    
102      @Column(name = "variation_value_1", updatable = true, nullable = true)
103      private Double variationValue1;
104    
105      @Column(name = "variation_value_2", updatable = true, nullable = true)
106      private Double variationValue2;
107    
108      @Column(name = "variation_value_3", updatable = true, nullable = true)
109      private Double variationValue3;
110    
111      @Column(name = "variation_value_4", updatable = true, nullable = true)
112      private Double variationValue4;
113    
114      @Column(name = "variation_value_5", updatable = true, nullable = true)
115      private Double variationValue5;
116    
117      @Column(name = "url", updatable = true, nullable = true, length = 2000)
118      private String url;
119    
120      @Column(name = "characteristic_id", nullable = true)
121      private Integer characteristicId;
122    
123      @Column(name = "person_id", updatable = true, nullable = true)
124      private Integer personId;
125    
126      @Column(name = "measure_data", updatable = true, nullable = true, length = 167772150)
127      private byte[] data;
128    
129      public Long getId() {
130        return id;
131      }
132    
133      public void setId(Long id) {
134        this.id = id;
135      }
136    
137      /**
138       * Creates a measure based on a metric and a double value
139       */
140      public MeasureModel(int metricId, Double val) {
141        if (val.isNaN() || val.isInfinite()) {
142          throw new IllegalArgumentException("Measure value is NaN. Metric=" + metricId);
143        }
144        this.metricId = metricId;
145        this.value = val;
146      }
147    
148      /**
149       * Creates a measure based on a metric and an alert level
150       */
151      public MeasureModel(int metricId, Metric.Level level) {
152        this.metricId = metricId;
153        if (level != null) {
154          this.textValue = level.toString();
155        }
156      }
157    
158      /**
159       * Creates a measure based on a metric and a string value
160       */
161      public MeasureModel(int metricId, String val) {
162        this.metricId = metricId;
163        setData(val);
164      }
165    
166      /**
167       * Creates an empty measure
168       */
169      public MeasureModel() {
170      }
171    
172      /**
173       * @return the measure double value
174       */
175      public Double getValue() {
176        return value;
177      }
178    
179      /**
180       * @return the measure description
181       */
182      public String getDescription() {
183        return description;
184      }
185    
186      /**
187       * Sets the measure description
188       */
189      public void setDescription(String description) {
190        this.description = description;
191      }
192    
193      /**
194       * Sets the measure value
195       *
196       * @throws IllegalArgumentException in case value is not a valid double
197       */
198      public MeasureModel setValue(Double value) {
199        if (value != null && (value.isNaN() || value.isInfinite())) {
200          throw new IllegalArgumentException();
201        }
202        this.value = value;
203        return this;
204      }
205    
206      /**
207       * @return the measure alert level
208       */
209      public Metric.Level getLevelValue() {
210        if (textValue != null) {
211          return Metric.Level.valueOf(textValue);
212        }
213        return null;
214      }
215    
216      /**
217       * Use getData() instead
218       */
219      public String getTextValue() {
220        return textValue;
221      }
222    
223      /**
224       * Use setData() instead
225       */
226      public void setTextValue(String textValue) {
227        this.textValue = textValue;
228      }
229    
230      /**
231       * @return the measure tendency
232       */
233      public Integer getTendency() {
234        return tendency;
235      }
236    
237      /**
238       * @return whether the measure is about rule
239       */
240      public boolean isRuleMeasure() {
241        return ruleId != null || rulePriority != null;
242      }
243    
244      /**
245       * Sets the measure tendency
246       *
247       * @return the current object
248       */
249      public MeasureModel setTendency(Integer tendency) {
250        this.tendency = tendency;
251        return this;
252      }
253    
254      public Integer getMetricId() {
255        return metricId;
256      }
257    
258      public void setMetricId(Integer metricId) {
259        this.metricId = metricId;
260      }
261    
262      /**
263       * @return the snapshot id the measure is attached to
264       */
265      public Integer getSnapshotId() {
266        return snapshotId;
267      }
268    
269      /**
270       * Sets the snapshot id
271       *
272       * @return the current object
273       */
274      public MeasureModel setSnapshotId(Integer snapshotId) {
275        this.snapshotId = snapshotId;
276        return this;
277      }
278    
279      public Integer getRuleId() {
280        return ruleId;
281      }
282    
283      /**
284       * Sets the rule for the measure
285       *
286       * @return the current object
287       */
288      public MeasureModel setRuleId(Integer ruleId) {
289        this.ruleId = ruleId;
290        return this;
291      }
292    
293      /**
294       * @return the rule priority
295       */
296      public RulePriority getRulePriority() {
297        return rulePriority;
298      }
299    
300      /**
301       * Sets the rule priority
302       */
303      public void setRulePriority(RulePriority rulePriority) {
304        this.rulePriority = rulePriority;
305      }
306    
307      /**
308       * @return the project id
309       */
310      public Integer getProjectId() {
311        return projectId;
312      }
313    
314      /**
315       * Sets the project id
316       */
317      public void setProjectId(Integer projectId) {
318        this.projectId = projectId;
319      }
320    
321      /**
322       * @return the date of the measure
323       */
324      public Date getMeasureDate() {
325        return measureDate;
326      }
327    
328      /**
329       * Sets the date for the measure
330       *
331       * @return the current object
332       */
333      public MeasureModel setMeasureDate(Date measureDate) {
334        this.measureDate = measureDate;
335        return this;
336      }
337    
338      /**
339       * @return the alert status if there is one, null otherwise
340       */
341      public Metric.Level getAlertStatus() {
342        if (alertStatus == null) {
343          return null;
344        }
345        return Metric.Level.valueOf(alertStatus);
346      }
347    
348      /**
349       * Sets the measure alert status
350       *
351       * @return the current object
352       */
353      public MeasureModel setAlertStatus(Metric.Level level) {
354        if (level != null) {
355          this.alertStatus = level.toString();
356        } else {
357          this.alertStatus = null;
358        }
359        return this;
360      }
361    
362      /**
363       * @return the measure data
364       */
365      public String getData(Metric metric) {
366        if (this.textValue != null) {
367          return this.textValue;
368        }
369        if (metric.isDataType() && data != null) {
370          try {
371            return new String(data, Charsets.UTF_8.name());
372          } catch (UnsupportedEncodingException e) {
373            // how is it possible to not support UTF-8 ?
374            Throwables.propagate(e);
375          }
376        }
377        return null;
378      }
379    
380      /**
381       * Sets the measure data
382       */
383      public final void setData(String data) {
384        if (data == null) {
385          this.textValue = null;
386          this.data = null;
387    
388        } else {
389          if (data.length() > TEXT_VALUE_LENGTH) {
390            this.textValue = null;
391            this.data = data.getBytes(Charsets.UTF_8);
392          } else {
393            this.textValue = data;
394            this.data = null;
395          }
396        }
397      }
398    
399      /**
400       * @return the text of the alert
401       */
402      public String getAlertText() {
403        return alertText;
404      }
405    
406      /**
407       * Sets the text for the alert
408       */
409      public void setAlertText(String alertText) {
410        this.alertText = alertText;
411      }
412    
413      /**
414       * @return the measure URL
415       */
416      public String getUrl() {
417        return url;
418      }
419    
420      /**
421       * Sets the measure URL
422       */
423      public void setUrl(String url) {
424        this.url = url;
425      }
426    
427      @Override
428      public String toString() {
429        return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
430      }
431    
432      public Double getVariationValue1() {
433        return variationValue1;
434      }
435    
436      public void setVariationValue1(Double d) {
437        this.variationValue1 = d;
438      }
439    
440      public Double getVariationValue2() {
441        return variationValue2;
442      }
443    
444      public void setVariationValue2(Double d) {
445        this.variationValue2 = d;
446      }
447    
448      public Double getVariationValue3() {
449        return variationValue3;
450      }
451    
452      public void setVariationValue3(Double d) {
453        this.variationValue3 = d;
454      }
455    
456      public Double getVariationValue4() {
457        return variationValue4;
458      }
459    
460      public void setVariationValue4(Double d) {
461        this.variationValue4 = d;
462      }
463    
464      public Double getVariationValue5() {
465        return variationValue5;
466      }
467    
468      public void setVariationValue5(Double d) {
469        this.variationValue5 = d;
470      }
471    
472      /**
473       * Saves the current object to database
474       *
475       * @return the current object
476       */
477      public MeasureModel save(DatabaseSession session) {
478        session.save(this);
479        return this;
480      }
481    
482      public Integer getCharacteristicId() {
483        return characteristicId;
484      }
485    
486      public MeasureModel setCharacteristicId(Integer characteristicId) {
487        this.characteristicId = characteristicId;
488        return this;
489      }
490    
491      public Integer getPersonId() {
492        return personId;
493      }
494    
495      public MeasureModel setPersonId(Integer i) {
496        this.personId = i;
497        return this;
498      }
499    
500      @Override
501      public Object clone() {
502        MeasureModel clone = new MeasureModel();
503        clone.setMetricId(getMetricId());
504        clone.setDescription(getDescription());
505        clone.setTextValue(getTextValue());
506        clone.setAlertStatus(getAlertStatus());
507        clone.setAlertText(getAlertText());
508        clone.setTendency(getTendency());
509        clone.setVariationValue1(getVariationValue1());
510        clone.setVariationValue2(getVariationValue2());
511        clone.setVariationValue3(getVariationValue3());
512        clone.setVariationValue4(getVariationValue4());
513        clone.setVariationValue5(getVariationValue5());
514        clone.setValue(getValue());
515        clone.setRulePriority(getRulePriority());
516        clone.setRuleId(getRuleId());
517        clone.setSnapshotId(getSnapshotId());
518        clone.setMeasureDate(getMeasureDate());
519        clone.setUrl(getUrl());
520        clone.setCharacteristicId(getCharacteristicId());
521        clone.setPersonId(getPersonId());
522        return clone;
523      }
524    
525    }