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.Column;
031import javax.persistence.Entity;
032import javax.persistence.EnumType;
033import javax.persistence.Enumerated;
034import javax.persistence.GeneratedValue;
035import javax.persistence.Id;
036import javax.persistence.Table;
037import javax.persistence.Temporal;
038import javax.persistence.TemporalType;
039
040import java.io.UnsupportedEncodingException;
041import 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")
048public 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}