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 */
020 package org.sonar.api.database.model;
021
022 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
023 import org.apache.commons.lang.builder.ToStringStyle;
024 import org.sonar.api.database.DatabaseSession;
025 import org.sonar.api.measures.Metric;
026 import org.sonar.api.qualitymodel.Characteristic;
027 import org.sonar.api.rules.RulePriority;
028
029 import javax.persistence.*;
030
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;//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 * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
288 */
289 @Deprecated
290 public Integer getRulesCategoryId() {
291 return null;
292 }
293
294 /**
295 * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
296 */
297 @Deprecated
298 public MeasureModel setRulesCategoryId(Integer id) {
299 return this;
300 }
301
302 /**
303 * @return the rule priority
304 */
305 public RulePriority getRulePriority() {
306 return rulePriority;
307 }
308
309 /**
310 * Sets the rule priority
311 */
312 public void setRulePriority(RulePriority rulePriority) {
313 this.rulePriority = rulePriority;
314 }
315
316 /**
317 * @return the project id
318 */
319 public Integer getProjectId() {
320 return projectId;
321 }
322
323 /**
324 * Sets the project id
325 */
326 public void setProjectId(Integer projectId) {
327 this.projectId = projectId;
328 }
329
330 /**
331 * @return the date of the measure
332 */
333 public Date getMeasureDate() {
334 return measureDate;
335 }
336
337 /**
338 * Sets the date for the measure
339 *
340 * @return the current object
341 */
342 public MeasureModel setMeasureDate(Date measureDate) {
343 this.measureDate = measureDate;
344 return this;
345 }
346
347 /**
348 * @return the alert status if there is one, null otherwise
349 */
350 public Metric.Level getAlertStatus() {
351 if (alertStatus == null) {
352 return null;
353 }
354 return Metric.Level.valueOf(alertStatus);
355 }
356
357 /**
358 * Sets the measure alert status
359 *
360 * @return the current object
361 */
362 public MeasureModel setAlertStatus(Metric.Level level) {
363 if (level != null) {
364 this.alertStatus = level.toString();
365 } else {
366 this.alertStatus = null;
367 }
368 return this;
369 }
370
371 /**
372 * @return the measure data
373 */
374 public String getData(Metric metric) {
375 if (this.textValue != null) {
376 return this.textValue;
377 }
378 if (metric.isDataType() && !measureData.isEmpty()) {
379 return measureData.get(0).getText();
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 measureData.clear();
391
392 } else {
393 if (data.length() > TEXT_VALUE_LENGTH) {
394 measureData.clear();
395 measureData.add(new MeasureData(this, data));
396
397 } else {
398 this.textValue = data;
399 }
400 }
401 }
402
403 /**
404 * Use getData() instead
405 */
406 public MeasureData getMeasureData() {
407 if (!measureData.isEmpty()) {
408 return measureData.get(0);
409 }
410 return null;
411 }
412
413 /**
414 * Use setData() instead
415 */
416 //@Deprecated
417 public void setMeasureData(MeasureData data) {
418 measureData.clear();
419 if (data != null) {
420 this.measureData.add(data);
421 }
422 }
423
424 /**
425 * @return the text of the alert
426 */
427 public String getAlertText() {
428 return alertText;
429 }
430
431 /**
432 * Sets the text for the alert
433 */
434 public void setAlertText(String alertText) {
435 this.alertText = alertText;
436 }
437
438 /**
439 * @return the measure URL
440 */
441 public String getUrl() {
442 return url;
443 }
444
445 /**
446 * Sets the measure URL
447 */
448 public void setUrl(String url) {
449 this.url = url;
450 }
451
452 @Override
453 public String toString() {
454 return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
455 }
456
457 public Double getVariationValue1() {
458 return variationValue1;
459 }
460
461 public void setVariationValue1(Double d) {
462 this.variationValue1 = d;
463 }
464
465 public Double getVariationValue2() {
466 return variationValue2;
467 }
468
469 public void setVariationValue2(Double d) {
470 this.variationValue2 = d;
471 }
472
473 public Double getVariationValue3() {
474 return variationValue3;
475 }
476
477 public void setVariationValue3(Double d) {
478 this.variationValue3 = d;
479 }
480
481 public Double getVariationValue4() {
482 return variationValue4;
483 }
484
485 public void setVariationValue4(Double d) {
486 this.variationValue4 = d;
487 }
488
489 public Double getVariationValue5() {
490 return variationValue5;
491 }
492
493 public void setVariationValue5(Double d) {
494 this.variationValue5 = d;
495 }
496
497 /**
498 * Saves the current object to database
499 *
500 * @return the current object
501 */
502 public MeasureModel save(DatabaseSession session) {
503 MeasureData data = getMeasureData();
504 setMeasureData(null);
505 session.save(this);
506
507 if (data != null) {
508 data.setMeasure(session.getEntity(MeasureModel.class, getId()));
509 data.setSnapshotId(snapshotId);
510 session.save(data);
511 setMeasureData(data);
512 }
513 return this;
514 }
515
516 public Characteristic getCharacteristic() {
517 return characteristic;
518 }
519
520 public MeasureModel setCharacteristic(Characteristic c) {
521 this.characteristic = c;
522 return this;
523 }
524
525 public Integer getPersonId() {
526 return personId;
527 }
528
529 public MeasureModel setPersonId(Integer i) {
530 this.personId = i;
531 return this;
532 }
533
534 @Override
535 public Object clone() {
536 MeasureModel clone = new MeasureModel();
537 clone.setMetricId(getMetricId());
538 clone.setDescription(getDescription());
539 clone.setTextValue(getTextValue());
540 clone.setAlertStatus(getAlertStatus());
541 clone.setAlertText(getAlertText());
542 clone.setTendency(getTendency());
543 clone.setVariationValue1(getVariationValue1());
544 clone.setVariationValue2(getVariationValue2());
545 clone.setVariationValue3(getVariationValue3());
546 clone.setVariationValue4(getVariationValue4());
547 clone.setVariationValue5(getVariationValue5());
548 clone.setValue(getValue());
549 clone.setRulePriority(getRulePriority());
550 clone.setRuleId(getRuleId());
551 clone.setSnapshotId(getSnapshotId());
552 clone.setMeasureDate(getMeasureDate());
553 clone.setUrl(getUrl());
554 clone.setCharacteristic(getCharacteristic());
555 clone.setPersonId(getPersonId());
556 return clone;
557 }
558
559 }