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