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