001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
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 */
020 package org.sonar.api.database.model;
021
022 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
023 import org.apache.commons.lang.builder.ToStringBuilder;
024 import org.apache.commons.lang.builder.ToStringStyle;
025 import org.sonar.api.database.DatabaseSession;
026 import org.sonar.api.measures.Metric;
027 import org.sonar.api.qualitymodel.Characteristic;
028 import org.sonar.api.rules.RulePriority;
029
030 import javax.persistence.*;
031 import java.util.ArrayList;
032 import java.util.Date;
033 import 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")
040 public 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;
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 public Long getId() {
120 return id;
121 }
122
123 public void setId(Long id) {
124 this.id = id;
125 }
126
127 /**
128 * Creates a measure based on a metric and a double value
129 */
130 public MeasureModel(int metricId, Double val) {
131 if (val.isNaN() || val.isInfinite()) {
132 throw new IllegalArgumentException("Measure value is NaN. Metric=" + metricId);
133 }
134 this.metricId = metricId;
135 this.value = val;
136 }
137
138 /**
139 * Creates a measure based on a metric and an alert level
140 */
141 public MeasureModel(int metricId, Metric.Level level) {
142 this.metricId = metricId;
143 if (level != null) {
144 this.textValue = level.toString();
145 }
146 }
147
148 /**
149 * Creates a measure based on a metric and a string value
150 */
151 public MeasureModel(int metricId, String val) {
152 this.metricId = metricId;
153 setData(val);
154 }
155
156 /**
157 * Creates an empty measure
158 */
159 public MeasureModel() {
160 }
161
162 /**
163 * @return the measure double value
164 */
165 public Double getValue() {
166 return value;
167 }
168
169 /**
170 * @return the measure description
171 */
172 public String getDescription() {
173 return description;
174 }
175
176 /**
177 * Sets the measure description
178 */
179 public void setDescription(String description) {
180 this.description = description;
181 }
182
183 /**
184 * Sets the measure value
185 *
186 * @throws IllegalArgumentException in case value is not a valid double
187 */
188 public MeasureModel setValue(Double value) throws IllegalArgumentException {
189 if (value != null && (value.isNaN() || value.isInfinite())) {
190 throw new IllegalArgumentException();
191 }
192 this.value = value;
193 return this;
194 }
195
196 /**
197 * @return the measure alert level
198 */
199 public Metric.Level getLevelValue() {
200 if (textValue != null) {
201 return Metric.Level.valueOf(textValue);
202 }
203 return null;
204 }
205
206 /**
207 * Use getData() instead
208 */
209 public String getTextValue() {
210 return textValue;
211 }
212
213 /**
214 * Use setData() instead
215 */
216 public void setTextValue(String textValue) {
217 this.textValue = textValue;
218 }
219
220 /**
221 * @return the measure tendency
222 */
223 public Integer getTendency() {
224 return tendency;
225 }
226
227 /**
228 * @return whether the measure is about rule
229 */
230 public boolean isRuleMeasure() {
231 return ruleId != null || rulePriority != null;
232 }
233
234 /**
235 * Sets the measure tendency
236 *
237 * @return the current object
238 */
239 public MeasureModel setTendency(Integer tendency) {
240 this.tendency = tendency;
241 return this;
242 }
243
244 public Integer getMetricId() {
245 return metricId;
246 }
247
248 public void setMetricId(Integer metricId) {
249 this.metricId = metricId;
250 }
251
252 /**
253 * @return the snapshot id the measure is attached to
254 */
255 public Integer getSnapshotId() {
256 return snapshotId;
257 }
258
259 /**
260 * Sets the snapshot id
261 *
262 * @return the current object
263 */
264 public MeasureModel setSnapshotId(Integer snapshotId) {
265 this.snapshotId = snapshotId;
266 return this;
267 }
268
269 public Integer getRuleId() {
270 return ruleId;
271 }
272
273 /**
274 * Sets the rule for the measure
275 *
276 * @return the current object
277 */
278 public MeasureModel setRuleId(Integer ruleId) {
279 this.ruleId = ruleId;
280 return this;
281 }
282
283 /**
284 * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
285 */
286 @Deprecated
287 public Integer getRulesCategoryId() {
288 return null;
289 }
290
291 /**
292 * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
293 */
294 @Deprecated
295 public MeasureModel setRulesCategoryId(Integer id) {
296 return this;
297 }
298
299 /**
300 * @return the rule priority
301 */
302 public RulePriority getRulePriority() {
303 return rulePriority;
304 }
305
306 /**
307 * Sets the rule priority
308 */
309 public void setRulePriority(RulePriority rulePriority) {
310 this.rulePriority = rulePriority;
311 }
312
313 /**
314 * @return the project id
315 */
316 public Integer getProjectId() {
317 return projectId;
318 }
319
320 /**
321 * Sets the project id
322 */
323 public void setProjectId(Integer projectId) {
324 this.projectId = projectId;
325 }
326
327 /**
328 * @return the date of the measure
329 */
330 public Date getMeasureDate() {
331 return measureDate;
332 }
333
334 /**
335 * Sets the date for the measure
336 *
337 * @return the current object
338 */
339 public MeasureModel setMeasureDate(Date measureDate) {
340 this.measureDate = measureDate;
341 return this;
342 }
343
344 /**
345 * @return the alert status if there is one, null otherwise
346 */
347 public Metric.Level getAlertStatus() {
348 if (alertStatus == null) {
349 return null;
350 }
351 return Metric.Level.valueOf(alertStatus);
352 }
353
354 /**
355 * Sets the measure alert status
356 *
357 * @return the current object
358 */
359 public MeasureModel setAlertStatus(Metric.Level level) {
360 if (level != null) {
361 this.alertStatus = level.toString();
362 } else {
363 this.alertStatus = null;
364 }
365 return this;
366 }
367
368 /**
369 * @return the measure data
370 */
371 public String getData(Metric metric) {
372 if (this.textValue != null) {
373 return this.textValue;
374 }
375 if (metric.isDataType() && !measureData.isEmpty()) {
376 return measureData.get(0).getText();
377 }
378 return null;
379 }
380
381 /**
382 * Sets the measure data
383 */
384 public final void setData(String data) {
385 if (data == null) {
386 this.textValue = null;
387 measureData.clear();
388
389 } else {
390 if (data.length() > TEXT_VALUE_LENGTH) {
391 measureData.clear();
392 measureData.add(new MeasureData(this, data));
393
394 } else {
395 this.textValue = data;
396 }
397 }
398 }
399
400 /**
401 * Use getData() instead
402 */
403 public MeasureData getMeasureData() {
404 if (!measureData.isEmpty()) {
405 return measureData.get(0);
406 }
407 return null;
408 }
409
410 /**
411 * Use setData() instead
412 */
413 //@Deprecated
414 public void setMeasureData(MeasureData data) {
415 measureData.clear();
416 if (data != null) {
417 this.measureData.add(data);
418 }
419 }
420
421 /**
422 * @return the text of the alert
423 */
424 public String getAlertText() {
425 return alertText;
426 }
427
428 /**
429 * Sets the text for the alert
430 */
431 public void setAlertText(String alertText) {
432 this.alertText = alertText;
433 }
434
435 /**
436 * @return the measure URL
437 */
438 public String getUrl() {
439 return url;
440 }
441
442 /**
443 * Sets the measure URL
444 */
445 public void setUrl(String url) {
446 this.url = url;
447 }
448
449 @Override
450 public String toString() {
451 return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
452 }
453
454 public Double getVariationValue1() {
455 return variationValue1;
456 }
457
458 public void setVariationValue1(Double d) {
459 this.variationValue1 = d;
460 }
461
462 public Double getVariationValue2() {
463 return variationValue2;
464 }
465
466 public void setVariationValue2(Double d) {
467 this.variationValue2 = d;
468 }
469
470 public Double getVariationValue3() {
471 return variationValue3;
472 }
473
474 public void setVariationValue3(Double d) {
475 this.variationValue3 = d;
476 }
477
478 public Double getVariationValue4() {
479 return variationValue4;
480 }
481
482 public void setVariationValue4(Double d) {
483 this.variationValue4 = d;
484 }
485
486 public Double getVariationValue5() {
487 return variationValue5;
488 }
489
490 public void setVariationValue5(Double d) {
491 this.variationValue5 = d;
492 }
493
494 /**
495 * Saves the current object to database
496 *
497 * @return the current object
498 */
499 public MeasureModel save(DatabaseSession session) {
500 MeasureData data = getMeasureData();
501 setMeasureData(null);
502 session.save(this);
503
504 if (data != null) {
505 data.setMeasure(session.getEntity(MeasureModel.class, getId()));
506 data.setSnapshotId(snapshotId);
507 session.save(data);
508 setMeasureData(data);
509 }
510 return this;
511 }
512
513 public Characteristic getCharacteristic() {
514 return characteristic;
515 }
516
517 public MeasureModel setCharacteristic(Characteristic c) {
518 this.characteristic = c;
519 return this;
520 }
521
522 @Override
523 public Object clone() {
524 MeasureModel clone = new MeasureModel();
525 clone.setMetricId(getMetricId());
526 clone.setDescription(getDescription());
527 clone.setTextValue(getTextValue());
528 clone.setAlertStatus(getAlertStatus());
529 clone.setAlertText(getAlertText());
530 clone.setTendency(getTendency());
531 clone.setVariationValue1(getVariationValue1());
532 clone.setVariationValue2(getVariationValue2());
533 clone.setVariationValue3(getVariationValue3());
534 clone.setVariationValue4(getVariationValue4());
535 clone.setVariationValue5(getVariationValue5());
536 clone.setValue(getValue());
537 clone.setRulePriority(getRulePriority());
538 clone.setRuleId(getRuleId());
539 clone.setSnapshotId(getSnapshotId());
540 clone.setMeasureDate(getMeasureDate());
541 clone.setUrl(getUrl());
542 clone.setCharacteristic(getCharacteristic());
543 return clone;
544 }
545 }