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