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.measures;
021
022 import org.apache.commons.lang.StringUtils;
023 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
024 import org.apache.commons.lang.builder.ToStringStyle;
025 import org.sonar.api.BatchExtension;
026 import org.sonar.api.ServerExtension;
027
028 import javax.persistence.*;
029
030 /**
031 * This class represents the definition of a metric in Sonar.
032 *
033 * @since 1.10
034 */
035 @Table(name = "metrics")
036 @Entity(name = "Metric")
037 public class Metric implements ServerExtension, BatchExtension {
038
039 /**
040 * A metric bigger value means a degradation
041 */
042 public static final int DIRECTION_WORST = -1;
043 /**
044 * A metric bigger value means an improvement
045 */
046 public static final int DIRECTION_BETTER = 1;
047 /**
048 * The metric direction has no meaning
049 */
050 public static final int DIRECTION_NONE = 0;
051
052 public enum ValueType {
053 INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB, RATING
054 }
055
056 public enum Level {
057 OK("Green"), WARN("Orange"), ERROR("Red");
058
059 private String colorName;
060
061 Level(String colorName) {
062 this.colorName = colorName;
063 }
064
065 public String getColorName() {
066 return colorName;
067 }
068 }
069
070 public enum Origin {
071 JAV, GUI, WS
072 }
073
074 @Id
075 @Column(name = "id")
076 @GeneratedValue
077 private Integer id;
078
079 @Transient
080 private Formula formula;
081
082 @Column(name = "name", updatable = false, nullable = false, length = 64)
083 private String key;
084
085 @Column(name = "description", updatable = true, nullable = true, length = 255)
086 private String description;
087
088 @Column(name = "val_type", updatable = true, nullable = true)
089 @Enumerated(EnumType.STRING)
090 private ValueType type;
091
092 @Column(name = "direction", updatable = true, nullable = true)
093 private Integer direction;
094
095 @Column(name = "domain", updatable = true, nullable = true, length = 60)
096 private String domain;
097
098 @Column(name = "short_name", updatable = true, nullable = true, length = 64)
099 private String name;
100
101 @Column(name = "qualitative", updatable = true, nullable = true)
102 private Boolean qualitative = Boolean.FALSE;
103
104 @Column(name = "user_managed", updatable = true, nullable = true)
105 private Boolean userManaged = Boolean.FALSE;
106
107 @Column(name = "enabled", updatable = true, nullable = true)
108 private Boolean enabled = Boolean.TRUE;
109
110 @Column(name = "origin", updatable = true, nullable = true, length = 3)
111 @Enumerated(EnumType.STRING)
112 private Origin origin = Origin.JAV;
113
114 @Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20)
115 private Double worstValue;
116
117 @Column(name = "best_value", updatable = true, nullable = true, precision = 30, scale = 20)
118 private Double bestValue;
119
120 @Column(name = "optimized_best_value", updatable = true, nullable = true)
121 private Boolean optimizedBestValue;
122
123 @Column(name = "hidden", updatable = true, nullable = true)
124 private Boolean hidden = Boolean.FALSE;
125
126 @Column(name = "delete_historical_data", updatable = true, nullable = true)
127 private Boolean deleteHistoricalData;
128
129 private Metric(Builder builder) {
130 this.key = builder.key;
131 this.name = builder.name;
132 this.description = builder.description;
133 this.type = builder.type;
134 this.direction = builder.direction;
135 this.domain = builder.domain;
136 this.qualitative = builder.qualitative;
137 this.enabled = Boolean.TRUE;
138 this.worstValue = builder.worstValue;
139 this.optimizedBestValue = builder.optimizedBestValue;
140 this.bestValue = builder.bestValue;
141 this.hidden = builder.hidden;
142 this.formula = builder.formula;
143 this.userManaged = builder.userManaged;
144 this.deleteHistoricalData = builder.deleteHistoricalData;
145 }
146
147 /**
148 * Creates an empty metric
149 *
150 * @deprecated in 1.12. Use the {@link Builder} factory.
151 */
152 @Deprecated
153 public Metric() {
154 }
155
156 /**
157 * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT)
158 *
159 * @param key the metric key
160 * @deprecated since 2.7 use the {@link Builder} factory.
161 */
162 @Deprecated
163 public Metric(String key) {
164 this(key, ValueType.INT);
165 }
166
167 /**
168 * Creates a metric based on a key and a type. Shortcut to
169 * Metric(key, key, key, type, -1, Boolean.FALSE, null, false)
170 *
171 * @param key the key
172 * @param type the type
173 * @deprecated since 2.7 use the {@link Builder} factory.
174 */
175 @Deprecated
176 public Metric(String key, ValueType type) {
177 this(key, key, key, type, -1, Boolean.FALSE, null, false);
178 }
179
180 /**
181 * @deprecated since 2.7 use the {@link Builder} factory.
182 */
183 @Deprecated
184 public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) {
185 this(key, name, description, type, direction, qualitative, domain, false);
186 }
187
188 /**
189 * Creates a fully qualified metric. This defaults some values:
190 * <ul>
191 * <li>origin : Origin.JAV</li>
192 * </ul>
193 *
194 * @param key the metric key
195 * @param name the metric name
196 * @param description the metric description
197 * @param type the metric type
198 * @param direction the metric direction
199 * @param qualitative whether the metric is qualitative
200 * @param domain the metric domain
201 * @param userManaged whether the metric is user managed
202 * @deprecated since 2.7 use the {@link Builder} factory.
203 */
204 @Deprecated
205 public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain,
206 boolean userManaged) {
207 this.key = key;
208 this.description = description;
209 this.type = type;
210 this.direction = direction;
211 this.domain = domain;
212 this.name = name;
213 this.qualitative = qualitative;
214 this.userManaged = userManaged;
215 this.origin = Origin.JAV;
216 if (ValueType.PERCENT.equals(this.type)) {
217 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
218 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
219 }
220 }
221
222 /**
223 * Creates a fully qualified metric. This defaults some values:
224 * <ul>
225 * <li>origin : Origin.JAV</li>
226 * <li>enabled : true</li>
227 * <li>userManaged : true</li>
228 * </ul>
229 *
230 * @param key the metric key
231 * @param name the metric name
232 * @param type the metric type
233 * @param direction the metric direction
234 * @param qualitative whether the metric is qualitative
235 * @param domain the metric domain
236 * @param formula the metric formula
237 * @deprecated since 2.7 use the {@link Builder} factory.
238 */
239 @Deprecated
240 public Metric(String key, String name, ValueType type, Integer direction, Boolean qualitative, String domain, Formula formula) {
241 this.key = key;
242 this.name = name;
243 this.type = type;
244 this.direction = direction;
245 this.domain = domain;
246 this.qualitative = qualitative;
247 this.origin = Origin.JAV;
248 this.enabled = true;
249 this.userManaged = false;
250 this.formula = formula;
251 if (ValueType.PERCENT.equals(this.type)) {
252 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
253 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
254 }
255 }
256
257 /**
258 * For internal use only
259 */
260 public Integer getId() {
261 return id;
262 }
263
264 /**
265 * For internal use only
266 */
267 public Metric setId(Integer id) {
268 this.id = id;
269 return this;
270 }
271
272 /**
273 * @return the metric formula
274 */
275 public Formula getFormula() {
276 return formula;
277 }
278
279 /**
280 * Sets the metric formula
281 *
282 * @param formula the formula
283 * @return this
284 */
285 public Metric setFormula(Formula formula) {
286 this.formula = formula;
287 return this;
288 }
289
290 /**
291 * @return wether the metric is qualitative
292 */
293 public Boolean getQualitative() {
294 return qualitative;
295 }
296
297 /**
298 * Sets whether the metric is qualitative
299 *
300 * @param qualitative whether the metric is qualitative
301 * @return this
302 */
303 public Metric setQualitative(Boolean qualitative) {
304 this.qualitative = qualitative;
305 return this;
306 }
307
308 /**
309 * @return the metric key
310 */
311 public String getKey() {
312 return key;
313 }
314
315 /**
316 * Sets the metric key
317 *
318 * @param key the key
319 * @return this
320 */
321 public Metric setKey(String key) {
322 this.key = key;
323 return this;
324 }
325
326 /**
327 * @return the metric type
328 */
329 public ValueType getType() {
330 return type;
331 }
332
333 /**
334 * Sets the metric type
335 *
336 * @param type the type
337 * @return this
338 */
339 public Metric setType(ValueType type) {
340 this.type = type;
341 return this;
342 }
343
344 /**
345 * @return the metric description
346 */
347 public String getDescription() {
348 return description;
349 }
350
351 /**
352 * Sets the metric description
353 *
354 * @param description the description
355 * @return this
356 */
357 public Metric setDescription(String description) {
358 this.description = description;
359 return this;
360 }
361
362 /**
363 * @return whether the metric is a managed by the users ("manual metric")
364 */
365 public Boolean getUserManaged() {
366 return userManaged;
367 }
368
369 /**
370 * Sets whether the metric is managed by users ("manual metric")
371 *
372 * @param userManaged whether the metric is user managed
373 * @return this
374 */
375 public Metric setUserManaged(Boolean userManaged) {
376 this.userManaged = userManaged;
377 return this;
378 }
379
380 /**
381 * @return whether the metric is enabled
382 */
383 public Boolean getEnabled() {
384 return enabled;
385 }
386
387 /**
388 * Sets whether the metric is enabled
389 *
390 * @param enabled whether the metric is enabled
391 * @return this
392 */
393 public Metric setEnabled(Boolean enabled) {
394 this.enabled = enabled;
395 return this;
396 }
397
398 /**
399 * @return the metric direction
400 */
401 public Integer getDirection() {
402 return direction;
403 }
404
405 /**
406 * Sets the metric direction.
407 *
408 * @param direction the direction
409 */
410 public Metric setDirection(Integer direction) {
411 this.direction = direction;
412 return this;
413 }
414
415 /**
416 * @return the domain of the metric
417 */
418 public String getDomain() {
419 return domain;
420 }
421
422 /**
423 * Sets the domain for the metric (General, Complexity...)
424 *
425 * @param domain the domain
426 * @return this
427 */
428 public Metric setDomain(String domain) {
429 this.domain = domain;
430 return this;
431 }
432
433 /**
434 * @return the metric name
435 */
436 public String getName() {
437 return name;
438 }
439
440 /**
441 * Sets the metric name
442 *
443 * @param name the name
444 * @return this
445 */
446 public Metric setName(String name) {
447 this.name = name;
448 return this;
449 }
450
451 /**
452 * @return the origin of the metric - Internal use only
453 */
454 public Origin getOrigin() {
455 return origin;
456 }
457
458 /**
459 * Set the origin of the metric - Internal use only
460 *
461 * @param origin the origin
462 * @return this
463 */
464 public Metric setOrigin(Origin origin) {
465 this.origin = origin;
466 return this;
467 }
468
469 public Double getWorstValue() {
470 return worstValue;
471 }
472
473 public Double getBestValue() {
474 return bestValue;
475 }
476
477 /**
478 * @return this
479 */
480 public Metric setWorstValue(Double d) {
481 this.worstValue = d;
482 return this;
483 }
484
485 /**
486 * @param bestValue the best value. It can be null.
487 * @return this
488 */
489 public Metric setBestValue(Double bestValue) {
490 this.bestValue = bestValue;
491 return this;
492 }
493
494 /**
495 * @return whether the metric is of a numeric type (int, percentage...)
496 */
497 public boolean isNumericType() {
498 return ValueType.INT.equals(type)
499 || ValueType.FLOAT.equals(type)
500 || ValueType.PERCENT.equals(type)
501 || ValueType.BOOL.equals(type)
502 || ValueType.MILLISEC.equals(type)
503 || ValueType.RATING.equals(type);
504 }
505
506 /**
507 * @return whether the metric is of type data
508 */
509 public boolean isDataType() {
510 return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type);
511 }
512
513 /**
514 * @return whether the metric is of type percentage
515 */
516 public boolean isPercentageType() {
517 return ValueType.PERCENT.equals(type);
518 }
519
520 public Metric setOptimizedBestValue(Boolean b) {
521 this.optimizedBestValue = b;
522 return this;
523 }
524
525 public Boolean isOptimizedBestValue() {
526 return optimizedBestValue;
527 }
528
529 public Boolean isHidden() {
530 return hidden;
531 }
532
533 public Metric setHidden(Boolean hidden) {
534 this.hidden = hidden;
535 return this;
536 }
537
538 public Boolean getDeleteHistoricalData() {
539 return deleteHistoricalData;
540 }
541
542 @Override
543 public int hashCode() {
544 return key.hashCode();
545 }
546
547 @Override
548 public boolean equals(Object obj) {
549 if (!(obj instanceof Metric)) {
550 return false;
551 }
552 if (this == obj) {
553 return true;
554 }
555 Metric other = (Metric) obj;
556 return key.equals(other.getKey());
557 }
558
559 @Override
560 public String toString() {
561 return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
562 }
563
564 /**
565 * Merge with fields from other metric. All fields are copied, except the id.
566 *
567 * @return this
568 */
569 public Metric merge(final Metric with) {
570 this.description = with.description;
571 this.domain = with.domain;
572 this.enabled = with.enabled;
573 this.qualitative = with.qualitative;
574 this.worstValue = with.worstValue;
575 this.bestValue = with.bestValue;
576 this.optimizedBestValue = with.optimizedBestValue;
577 this.direction = with.direction;
578 this.key = with.key;
579 this.type = with.type;
580 this.name = with.name;
581 this.userManaged = with.userManaged;
582 this.origin = with.origin;
583 this.hidden = with.hidden;
584 this.deleteHistoricalData = with.deleteHistoricalData;
585 return this;
586 }
587
588 /**
589 * Metric.Builder is used to create metric definitions. It must be preferred to creating new instances of the Metric class directly.
590 *
591 * @since 2.7
592 */
593 public static final class Builder {
594 private String key;
595 private Metric.ValueType type;
596 private String name;
597 private String description;
598 private Integer direction = DIRECTION_NONE;
599 private Boolean qualitative = Boolean.FALSE;
600 private String domain = null;
601 private Formula formula;
602 private Double worstValue;
603 private Double bestValue;
604 private boolean optimizedBestValue = false;
605 private boolean hidden = false;
606 private boolean userManaged = false;
607 private boolean deleteHistoricalData = false;
608
609 /**
610 * Creates a new {@link Builder} object.
611 *
612 * @param key the metric key, should be unique among all metrics
613 * @param name the metric name
614 * @param type the metric type
615 */
616 public Builder(String key, String name, ValueType type) {
617 if (StringUtils.isBlank(key)) {
618 throw new IllegalArgumentException("Metric key can not be blank");
619 }
620 if (StringUtils.isBlank(name)) {
621 throw new IllegalArgumentException("Metric name can not be blank");
622 }
623 if (type == null) {
624 throw new IllegalArgumentException("Metric type can not be null");
625 }
626 this.key = key;
627 this.name = name;
628 this.type = type;
629 }
630
631 /**
632 * Sets the metric description.
633 *
634 * @param d the description
635 * @return the builder
636 */
637 public Builder setDescription(String d) {
638 this.description = d;
639 return this;
640 }
641
642 /**
643 * Sets the metric direction (used for numeric values only), which is used in the Web UI to show if the trend of a metric is good or not.
644 * <ul>
645 * <li>Metric.DIRECTION_WORST: indicates that an increase of the metric value is not a good thing (example: the complexity of a function)</li>
646 * <li>Metric.DIRECTION_BETTER: indicates that an increase of the metric value is a good thing (example: the code coverage of a function)</li>
647 * <li>Metric.DIRECTION_NONE: indicates that the variation of the metric value is neither good nor bad (example: number of files).</li>
648 * </ul>
649 * Metric.DIRECTION_NONE is the default value.
650 *
651 * @see Metric#DIRECTION_WORST
652 * @see Metric#DIRECTION_BETTER
653 * @see Metric#DIRECTION_NONE
654 *
655 * @param d the direction
656 * @return the builder
657 */
658 public Builder setDirection(Integer d) {
659 this.direction = d;
660 return this;
661 }
662
663 /**
664 * Sets whether the metric is qualitative or not. Default value is false.
665 * <br/>
666 * If set to true, then variations of this metric will be highlighted in the Web UI (for instance, trend icons will be red or green instead of default grey).
667 *
668 * @param b Boolean.TRUE if the metric is qualitative
669 * @return the builder
670 */
671 public Builder setQualitative(Boolean b) {
672 this.qualitative = b;
673 return this;
674 }
675
676 /**
677 * Sets the domain for the metric (General, Complexity...). This is used to group metrics in the Web UI.
678 * <br/>
679 * By default, the metric belongs to no specific domain.
680 *
681 * @param d the domain
682 * @return the builder
683 */
684 public Builder setDomain(String d) {
685 this.domain = d;
686 return this;
687 }
688
689 /**
690 * Specifies the formula used by Sonar to automatically aggregate measures stored on files up to the project level.
691 * <br/>
692 * <br/>
693 * By default, no formula is defined, which means that it's up to a sensor/decorator to compute measures on appropriate levels.
694 * <br/>
695 * When a formula is set, sensors/decorators just need to store measures at a specific level and let Sonar run the formula to store
696 * measures on the remaining levels.
697 *
698 * @see {@link SumChildDistributionFormula}, {@link SumChildValuesFormula}, {@link AverageComplexityFormula}, {@link MeanAggregationFormula},
699 * {@link WeightedMeanAggregationFormula}
700 *
701 * @param f the formula
702 * @return the builder
703 */
704 public Builder setFormula(Formula f) {
705 this.formula = f;
706 return this;
707 }
708
709 /**
710 * Sets the worst value that the metric can get (example: 0.0 for code coverage). No worst value is set by default.
711 *
712 * @param d the worst value
713 * @return the builder
714 */
715 public Builder setWorstValue(Double d) {
716 this.worstValue = d;
717 return this;
718 }
719
720 /**
721 * Sets the best value that the metric can get (example: 100.0 for code coverage). No best value is set by default.
722 * <br/>
723 * Resources would be hidden on drilldown page, if the value of measure equals to best value.
724 *
725 * @param d the best value
726 * @return the builder
727 */
728 public Builder setBestValue(Double d) {
729 this.bestValue = d;
730 return this;
731 }
732
733 /**
734 * Specifies whether file-level measures that equal to the defined best value are stored or not. Default is false.
735 * <br/>
736 * Example with the metric that stores the number of violation ({@link CoreMetrics#VIOLATIONS}):
737 * if a file has no violation, then the value '0' won't be stored in the database.
738 *
739 * @param b true if the measures must not be stored when they equal to the best value
740 * @return the builder
741 */
742 public Builder setOptimizedBestValue(boolean b) {
743 this.optimizedBestValue = b;
744 return this;
745 }
746
747 /**
748 * Sets whether the metric should be hidden in Web UI (e.g. in Time Machine). Default is false.
749 *
750 * @param b true if the metric should be hidden.
751 * @return the builder
752 */
753 public Builder setHidden(boolean b) {
754 this.hidden = b;
755 return this;
756 }
757
758 /**
759 * Specifies whether this metric can be edited online in the "Manual measures" page. Default is false.
760 *
761 * @since 2.10
762 *
763 * @param b true if the metric can be edited online.
764 * @return the builder
765 */
766 public Builder setUserManaged(boolean b) {
767 this.userManaged = b;
768 return this;
769 }
770
771 /**
772 * Specifies whether measures from the past can be automatically deleted to minimize database volume.
773 * <br/>
774 * By default, historical data are kept.
775 *
776 * @since 2.14
777 *
778 * @param b true if measures from the past can be deleted automatically.
779 * @return the builder
780 */
781 public Builder setDeleteHistoricalData(boolean b) {
782 this.deleteHistoricalData = b;
783 return this;
784 }
785
786 /**
787 * Creates a new metric definition based on the properties set on this metric builder.
788 *
789 * @return a new {@link Metric} object
790 */
791 public Metric create() {
792 if (ValueType.PERCENT.equals(this.type)) {
793 this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
794 this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
795 }
796 return new Metric(this);
797 }
798 }
799 }