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