001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 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.ToStringBuilder;
024    import org.sonar.api.BatchExtension;
025    import org.sonar.api.ServerExtension;
026    
027    import javax.persistence.*;
028    
029    /**
030     * @since 1.10
031     */
032    @Table(name = "metrics")
033    @Entity(name = "Metric")
034    public class Metric implements ServerExtension, BatchExtension {
035    
036      /**
037       * A metric bigger value means a degradation
038       */
039      public final static int DIRECTION_WORST = -1;
040      /**
041       * A metric bigger value means an improvement
042       */
043      public final static int DIRECTION_BETTER = 1;
044      /**
045       * The metric direction has no meaning
046       */
047      public final static int DIRECTION_NONE = 0;
048    
049      public enum ValueType {
050        INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB, RATING
051      }
052    
053      public enum Level {
054        OK("Green"), WARN("Orange"), ERROR("Red");
055    
056        private String colorName;
057    
058        Level(String colorName) {
059          this.colorName = colorName;
060        }
061    
062        public String getColorName() {
063          return colorName;
064        }
065      }
066    
067      public enum Origin {
068        JAV, GUI, WS
069      }
070    
071      @Id
072      @Column(name = "id")
073      @GeneratedValue
074      private Integer id;
075    
076      @Transient
077      private Formula formula;
078    
079      @Column(name = "name", updatable = false, nullable = false, length = 64)
080      private String key;
081    
082      @Column(name = "description", updatable = true, nullable = true, length = 255)
083      private String description;
084    
085      @Column(name = "val_type", updatable = true, nullable = true)
086      @Enumerated(EnumType.STRING)
087      private ValueType type;
088    
089      @Column(name = "direction", updatable = true, nullable = true)
090      private Integer direction;
091    
092      @Column(name = "domain", updatable = true, nullable = true, length = 60)
093      private String domain;
094    
095      @Column(name = "short_name", updatable = true, nullable = true, length = 64)
096      private String name;
097    
098      @Column(name = "qualitative", updatable = true, nullable = true)
099      private Boolean qualitative = Boolean.FALSE;
100    
101      @Column(name = "user_managed", updatable = true, nullable = true)
102      private Boolean userManaged = Boolean.FALSE;
103    
104      @Column(name = "enabled", updatable = true, nullable = true)
105      private Boolean enabled = Boolean.TRUE;
106    
107      @Column(name = "origin", updatable = true, nullable = true, length = 3)
108      @Enumerated(EnumType.STRING)
109      private Origin origin = Origin.JAV;
110    
111      @Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20)
112      private Double worstValue;
113    
114      @Column(name = "best_value", updatable = true, nullable = true, precision = 30, scale = 20)
115      private Double bestValue;
116    
117      @Column(name = "optimized_best_value", updatable = true, nullable = true)
118      private Boolean optimizedBestValue;
119    
120      @Column(name = "hidden", updatable = true, nullable = true)
121      private Boolean hidden = Boolean.FALSE;
122    
123      /**
124       * Creates an empty metric
125       * 
126       * @deprecated in 1.12. Use the {@link Builder} factory.
127       */
128      @Deprecated
129      public Metric() {
130      }
131    
132      /**
133       * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT)
134       *
135       * @param key the metric key
136       * @deprecated since 2.7 use the {@link Builder} factory.
137       */
138      @Deprecated
139      public Metric(String key) {
140        this(key, ValueType.INT);
141      }
142    
143      /**
144       * Creates a metric based on a key and a type. Shortcut to
145       * Metric(key, key, key, type, -1, Boolean.FALSE, null, false)
146       *
147       * @param key  the key
148       * @param type the type
149       * @deprecated since 2.7 use the {@link Builder} factory.
150       */
151      @Deprecated
152      public Metric(String key, ValueType type) {
153        this(key, key, key, type, -1, Boolean.FALSE, null, false);
154      }
155    
156      /**
157       * @deprecated since 2.7 use the {@link Builder} factory.
158       */
159      @Deprecated
160      public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) {
161        this(key, name, description, type, direction, qualitative, domain, false);
162      }
163    
164      /**
165       * Creates a fully qualified metric. This defaults some values:
166       * <ul>
167       * <li>origin : Origin.JAV</li>
168       * </ul>
169       *
170       * @param key         the metric key
171       * @param name        the metric name
172       * @param description the metric description
173       * @param type        the metric type
174       * @param direction   the metric direction
175       * @param qualitative whether the metric is qualitative
176       * @param domain      the metric domain
177       * @param userManaged whether the metric is user managed
178       * @deprecated since 2.7 use the {@link Builder} factory.
179       */
180      @Deprecated
181      public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain, boolean userManaged) {
182        this.key = key;
183        this.description = description;
184        this.type = type;
185        this.direction = direction;
186        this.domain = domain;
187        this.name = name;
188        this.qualitative = qualitative;
189        this.userManaged = userManaged;
190        this.origin = Origin.JAV;
191        if (ValueType.PERCENT.equals(this.type)) {
192          this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
193          this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
194        }
195      }
196    
197      /**
198       * Creates a fully qualified metric. This defaults some values:
199       * <ul>
200       * <li>origin : Origin.JAV</li>
201       * <li>enabled : true</li>
202       * <li>userManaged : true</li>
203       * </ul>
204       *
205       * @param key         the metric key
206       * @param name        the metric name
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 formula     the metric formula
212       * @deprecated since 2.7 use the {@link Builder} factory.
213       */
214      @Deprecated
215      public Metric(String key, String name, ValueType type, Integer direction, Boolean qualitative, String domain, Formula formula) {
216        this.key = key;
217        this.name = name;
218        this.type = type;
219        this.direction = direction;
220        this.domain = domain;
221        this.qualitative = qualitative;
222        this.origin = Origin.JAV;
223        this.enabled = true;
224        this.userManaged = false;
225        this.formula = formula;
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      private Metric(String key, String name, ValueType type, String description, Integer direction, String domain, Boolean qualitative, Double worstValue, Double bestValue,
233          Boolean optimizedBestValue, Boolean hidden, Formula formula) {
234        this.key = key;
235        this.name = name;
236        this.description = description;
237        this.type = type;
238        this.direction = direction;
239        this.domain = domain;
240        this.qualitative = qualitative;
241        this.userManaged = Boolean.FALSE;
242        this.enabled = Boolean.TRUE;
243        this.worstValue = worstValue;
244        this.optimizedBestValue = optimizedBestValue;
245        this.bestValue = bestValue;
246        this.hidden = hidden;
247        this.formula = formula;
248      }
249    
250      /**
251       * For internal use only
252       */
253      public Integer getId() {
254        return id;
255      }
256    
257      /**
258       * For internal use only
259       */
260      public Metric setId(Integer id) {
261        this.id = id;
262        return this;
263      }
264    
265      /**
266       * @return the metric formula
267       */
268      public Formula getFormula() {
269        return formula;
270      }
271    
272      /**
273       * Sets the metric formula
274       *
275       * @param formula the formula
276       * @return this
277       */
278      public Metric setFormula(Formula formula) {
279        this.formula = formula;
280        return this;
281      }
282    
283      /**
284       * @return wether the metric is qualitative
285       */
286      public Boolean getQualitative() {
287        return qualitative;
288      }
289    
290      /**
291       * Sets whether the metric is qualitative
292       *
293       * @param qualitative whether the metric is qualitative
294       * @return this
295       */
296      public Metric setQualitative(Boolean qualitative) {
297        this.qualitative = qualitative;
298        return this;
299      }
300    
301      /**
302       * @return the metric key
303       */
304      public String getKey() {
305        return key;
306      }
307    
308      /**
309       * Sets the metric key
310       *
311       * @param key the key
312       * @return this
313       */
314      public Metric setKey(String key) {
315        this.key = key;
316        return this;
317      }
318    
319      /**
320       * @return the metric type
321       */
322      public ValueType getType() {
323        return type;
324      }
325    
326      /**
327       * Sets the metric type
328       *
329       * @param type the type
330       * @return this
331       */
332      public Metric setType(ValueType type) {
333        this.type = type;
334        return this;
335      }
336    
337      /**
338       * @return the metric description
339       */
340      public String getDescription() {
341        return description;
342      }
343    
344      /**
345       * Sets the metric description
346       *
347       * @param description the description
348       * @return this
349       */
350      public Metric setDescription(String description) {
351        this.description = description;
352        return this;
353      }
354    
355      /**
356       * @return whether the metric is a managed by the users (manual metric)
357       */
358      public Boolean getUserManaged() {
359        return userManaged;
360      }
361    
362      /**
363       * Sets whether the metric is user managed
364       *
365       * @param userManaged whether the metric is user managed
366       * @return this
367       */
368      public Metric setUserManaged(Boolean userManaged) {
369        this.userManaged = userManaged;
370        return this;
371      }
372    
373      /**
374       * @return whether the metric is enabled
375       */
376      public Boolean getEnabled() {
377        return enabled;
378      }
379    
380      /**
381       * Sets whether the metric is enabled
382       *
383       * @param enabled whether the metric is enabled
384       * @return this
385       */
386      public Metric setEnabled(Boolean enabled) {
387        this.enabled = enabled;
388        return this;
389      }
390    
391      /**
392       * @return the metric direction
393       */
394      public Integer getDirection() {
395        return direction;
396      }
397    
398      /**
399       * Sets the metric direction.
400       *
401       * @param direction the direction
402       */
403      public Metric setDirection(Integer direction) {
404        this.direction = direction;
405        return this;
406      }
407    
408      /**
409       * @return the domain of the metric
410       */
411      public String getDomain() {
412        return domain;
413      }
414    
415      /**
416       * Sets the domain for the metric (General, Complexity...)
417       *
418       * @param domain the domain
419       * @return this
420       */
421      public Metric setDomain(String domain) {
422        this.domain = domain;
423        return this;
424      }
425    
426      /**
427       * @return the metric name
428       */
429      public String getName() {
430        return name;
431      }
432    
433      /**
434       * Sets the metric name
435       *
436       * @param name the name
437       * @return this
438       */
439      public Metric setName(String name) {
440        this.name = name;
441        return this;
442      }
443    
444      /**
445       * @return the origin of the metric - Internal use only
446       */
447      public Origin getOrigin() {
448        return origin;
449      }
450    
451      /**
452       * Set the origin of the metric - Internal use only
453       *
454       * @param origin the origin
455       * @return this
456       */
457      public Metric setOrigin(Origin origin) {
458        this.origin = origin;
459        return this;
460      }
461    
462      public Double getWorstValue() {
463        return worstValue;
464      }
465    
466      public Double getBestValue() {
467        return bestValue;
468      }
469    
470      /**
471       * @return this
472       */
473      public Metric setWorstValue(Double d) {
474        this.worstValue = d;
475        return this;
476      }
477    
478      /**
479       * @param bestValue the best value. It can be null.
480       * @return this
481       */
482      public Metric setBestValue(Double bestValue) {
483        this.bestValue = bestValue;
484        return this;
485      }
486    
487      /**
488       * @return whether the metric is of a numeric type (int, percentage...)
489       */
490      public boolean isNumericType() {
491        return ValueType.INT.equals(type)
492            || ValueType.FLOAT.equals(type)
493            || ValueType.PERCENT.equals(type)
494            || ValueType.BOOL.equals(type)
495            || ValueType.MILLISEC.equals(type)
496            || ValueType.RATING.equals(type);
497      }
498    
499      /**
500       * @return whether the metric is of type data
501       */
502      public boolean isDataType() {
503        return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type);
504      }
505    
506      /**
507       * @return whether the metric is of type percentage
508       */
509      public boolean isPercentageType() {
510        return ValueType.PERCENT.equals(type);
511      }
512    
513      public Metric setOptimizedBestValue(Boolean b) {
514        this.optimizedBestValue = b;
515        return this;
516      }
517    
518      public Boolean isOptimizedBestValue() {
519        return optimizedBestValue;
520      }
521    
522      public Boolean isHidden() {
523        return hidden;
524      }
525    
526      public Metric setHidden(Boolean hidden) {
527        this.hidden = hidden;
528        return this;
529      }
530    
531      @Override
532      public int hashCode() {
533        return key.hashCode();
534      }
535    
536      @Override
537      public boolean equals(Object obj) {
538        if (!(obj instanceof Metric)) {
539          return false;
540        }
541        if (this == obj) {
542          return true;
543        }
544        Metric other = (Metric) obj;
545        return key.equals(other.getKey());
546      }
547    
548      @Override
549      public String toString() {
550        return new ToStringBuilder(this)
551            .append("key", key)
552            .append("name", name)
553            .append("type", type)
554            .append("enabled", enabled)
555            .append("qualitative", qualitative)
556            .append("direction", direction)
557            .append("domain", domain)
558            .append("worstValue", worstValue)
559            .append("bestValue", bestValue)
560            .append("optimizedBestValue", optimizedBestValue)
561            .append("hidden", hidden)
562            .toString();
563      }
564    
565      /**
566       * Merge with fields from other metric. All fields are copied, except the id.
567       *
568       * @return this
569       */
570      public Metric merge(final Metric with) {
571        this.description = with.description;
572        this.domain = with.domain;
573        this.enabled = with.enabled;
574        this.qualitative = with.qualitative;
575        this.worstValue = with.worstValue;
576        this.bestValue = with.bestValue;
577        this.optimizedBestValue = with.optimizedBestValue;
578        this.direction = with.direction;
579        this.key = with.key;
580        this.type = with.type;
581        this.name = with.name;
582        this.userManaged = with.userManaged;
583        this.origin = with.origin;
584        this.hidden = with.hidden;
585        return this;
586      }
587    
588      /**
589       * @since 2.7
590       */
591      public static final class Builder {
592        private String key;
593        private Metric.ValueType type;
594        private String name;
595        private String description;
596        private Integer direction = DIRECTION_NONE;
597        private Boolean qualitative = Boolean.FALSE;
598        private String domain = null;
599        private Formula formula;
600        private Double worstValue;
601        private Double bestValue;
602        private boolean optimizedBestValue = false;
603        private boolean hidden = false;
604    
605        /**
606         * @param key the metric key, should be unique among all metrics
607         * @param name the metric name
608         * @param type the metric type
609         */
610        public Builder(String key, String name, ValueType type) {
611          if (StringUtils.isBlank(key)) {
612            throw new IllegalArgumentException("Metric key can not be blank");
613          }
614          if (StringUtils.isBlank(name)) {
615            throw new IllegalArgumentException("Metric name can not be blank");
616          }
617          if (type == null) {
618            throw new IllegalArgumentException("Metric type can not be null");
619          }
620          this.key = key;
621          this.name = name;
622          this.type = type;
623        }
624    
625        /**
626         * Sets the metric description.
627         */
628        public Builder setDescription(String s) {
629          this.description = s;
630          return this;
631        }
632    
633        /**
634         * Sets the metric direction. Used for numeric values only.
635         *
636         * @see Metric#DIRECTION_WORST
637         * @see Metric#DIRECTION_BETTER
638         * @see Metric#DIRECTION_NONE
639         */
640        public Builder setDirection(Integer i) {
641          this.direction = i;
642          return this;
643        }
644    
645        /**
646         * Sets whether the metric is qualitative.
647         */
648        public Builder setQualitative(Boolean b) {
649          this.qualitative = b;
650          return this;
651        }
652    
653        /**
654         * Sets the domain for the metric (General, Complexity...).
655         */
656        public Builder setDomain(String s) {
657          this.domain = s;
658          return this;
659        }
660    
661        public Builder setFormula(Formula f) {
662          this.formula = f;
663          return this;
664        }
665    
666        /**
667         * Sets the worst value.
668         */
669        public Builder setWorstValue(Double d) {
670          this.worstValue = d;
671          return this;
672        }
673    
674        /**
675         * Sets the best value. Resources would be hidden on drilldown page, if value of measure equal to best value.
676         */
677        public Builder setBestValue(Double d) {
678          this.bestValue = d;
679          return this;
680        }
681    
682        public Builder setOptimizedBestValue(boolean b) {
683          this.optimizedBestValue = b;
684          return this;
685        }
686    
687        /**
688         * Sets whether the metric should be hidden in UI (e.g. in Time Machine).
689         */
690        public Builder setHidden(boolean b) {
691          this.hidden = b;
692          return this;
693        }
694    
695        public Metric create() {
696          if (ValueType.PERCENT.equals(this.type)) {
697            this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
698            this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
699          }
700          return new Metric(key, name, type, description, direction, domain, qualitative, worstValue, bestValue, optimizedBestValue, hidden, formula);
701        }
702      }
703    }