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