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