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