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