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    }