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    }