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, boolean userManaged) {
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        this.userManaged = userManaged;
249      }
250    
251      /**
252       * For internal use only
253       */
254      public Integer getId() {
255        return id;
256      }
257    
258      /**
259       * For internal use only
260       */
261      public Metric setId(Integer id) {
262        this.id = id;
263        return this;
264      }
265    
266      /**
267       * @return the metric formula
268       */
269      public Formula getFormula() {
270        return formula;
271      }
272    
273      /**
274       * Sets the metric formula
275       *
276       * @param formula the formula
277       * @return this
278       */
279      public Metric setFormula(Formula formula) {
280        this.formula = formula;
281        return this;
282      }
283    
284      /**
285       * @return wether the metric is qualitative
286       */
287      public Boolean getQualitative() {
288        return qualitative;
289      }
290    
291      /**
292       * Sets whether the metric is qualitative
293       *
294       * @param qualitative whether the metric is qualitative
295       * @return this
296       */
297      public Metric setQualitative(Boolean qualitative) {
298        this.qualitative = qualitative;
299        return this;
300      }
301    
302      /**
303       * @return the metric key
304       */
305      public String getKey() {
306        return key;
307      }
308    
309      /**
310       * Sets the metric key
311       *
312       * @param key the key
313       * @return this
314       */
315      public Metric setKey(String key) {
316        this.key = key;
317        return this;
318      }
319    
320      /**
321       * @return the metric type
322       */
323      public ValueType getType() {
324        return type;
325      }
326    
327      /**
328       * Sets the metric type
329       *
330       * @param type the type
331       * @return this
332       */
333      public Metric setType(ValueType type) {
334        this.type = type;
335        return this;
336      }
337    
338      /**
339       * @return the metric description
340       */
341      public String getDescription() {
342        return description;
343      }
344    
345      /**
346       * Sets the metric description
347       *
348       * @param description the description
349       * @return this
350       */
351      public Metric setDescription(String description) {
352        this.description = description;
353        return this;
354      }
355    
356      /**
357       * @return whether the metric is a managed by the users ("manual metric")
358       */
359      public Boolean getUserManaged() {
360        return userManaged;
361      }
362    
363      /**
364       * Sets whether the metric is managed by users ("manual metric")
365       *
366       * @param userManaged whether the metric is user managed
367       * @return this
368       */
369      public Metric setUserManaged(Boolean userManaged) {
370        this.userManaged = userManaged;
371        return this;
372      }
373    
374      /**
375       * @return whether the metric is enabled
376       */
377      public Boolean getEnabled() {
378        return enabled;
379      }
380    
381      /**
382       * Sets whether the metric is enabled
383       *
384       * @param enabled whether the metric is enabled
385       * @return this
386       */
387      public Metric setEnabled(Boolean enabled) {
388        this.enabled = enabled;
389        return this;
390      }
391    
392      /**
393       * @return the metric direction
394       */
395      public Integer getDirection() {
396        return direction;
397      }
398    
399      /**
400       * Sets the metric direction.
401       *
402       * @param direction the direction
403       */
404      public Metric setDirection(Integer direction) {
405        this.direction = direction;
406        return this;
407      }
408    
409      /**
410       * @return the domain of the metric
411       */
412      public String getDomain() {
413        return domain;
414      }
415    
416      /**
417       * Sets the domain for the metric (General, Complexity...)
418       *
419       * @param domain the domain
420       * @return this
421       */
422      public Metric setDomain(String domain) {
423        this.domain = domain;
424        return this;
425      }
426    
427      /**
428       * @return the metric name
429       */
430      public String getName() {
431        return name;
432      }
433    
434      /**
435       * Sets the metric name
436       *
437       * @param name the name
438       * @return this
439       */
440      public Metric setName(String name) {
441        this.name = name;
442        return this;
443      }
444    
445      /**
446       * @return the origin of the metric - Internal use only
447       */
448      public Origin getOrigin() {
449        return origin;
450      }
451    
452      /**
453       * Set the origin of the metric - Internal use only
454       *
455       * @param origin the origin
456       * @return this
457       */
458      public Metric setOrigin(Origin origin) {
459        this.origin = origin;
460        return this;
461      }
462    
463      public Double getWorstValue() {
464        return worstValue;
465      }
466    
467      public Double getBestValue() {
468        return bestValue;
469      }
470    
471      /**
472       * @return this
473       */
474      public Metric setWorstValue(Double d) {
475        this.worstValue = d;
476        return this;
477      }
478    
479      /**
480       * @param bestValue the best value. It can be null.
481       * @return this
482       */
483      public Metric setBestValue(Double bestValue) {
484        this.bestValue = bestValue;
485        return this;
486      }
487    
488      /**
489       * @return whether the metric is of a numeric type (int, percentage...)
490       */
491      public boolean isNumericType() {
492        return ValueType.INT.equals(type)
493            || ValueType.FLOAT.equals(type)
494            || ValueType.PERCENT.equals(type)
495            || ValueType.BOOL.equals(type)
496            || ValueType.MILLISEC.equals(type)
497            || ValueType.RATING.equals(type);
498      }
499    
500      /**
501       * @return whether the metric is of type data
502       */
503      public boolean isDataType() {
504        return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type);
505      }
506    
507      /**
508       * @return whether the metric is of type percentage
509       */
510      public boolean isPercentageType() {
511        return ValueType.PERCENT.equals(type);
512      }
513    
514      public Metric setOptimizedBestValue(Boolean b) {
515        this.optimizedBestValue = b;
516        return this;
517      }
518    
519      public Boolean isOptimizedBestValue() {
520        return optimizedBestValue;
521      }
522    
523      public Boolean isHidden() {
524        return hidden;
525      }
526    
527      public Metric setHidden(Boolean hidden) {
528        this.hidden = hidden;
529        return this;
530      }
531    
532      @Override
533      public int hashCode() {
534        return key.hashCode();
535      }
536    
537      @Override
538      public boolean equals(Object obj) {
539        if (!(obj instanceof Metric)) {
540          return false;
541        }
542        if (this == obj) {
543          return true;
544        }
545        Metric other = (Metric) obj;
546        return key.equals(other.getKey());
547      }
548    
549      @Override
550      public String toString() {
551        return new ToStringBuilder(this)
552            .append("key", key)
553            .append("name", name)
554            .append("type", type)
555            .append("enabled", enabled)
556            .append("qualitative", qualitative)
557            .append("direction", direction)
558            .append("domain", domain)
559            .append("worstValue", worstValue)
560            .append("bestValue", bestValue)
561            .append("optimizedBestValue", optimizedBestValue)
562            .append("hidden", hidden)
563            .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        return this;
587      }
588    
589      /**
590       * @since 2.7
591       */
592      public static final class Builder {
593        private String key;
594        private Metric.ValueType type;
595        private String name;
596        private String description;
597        private Integer direction = DIRECTION_NONE;
598        private Boolean qualitative = Boolean.FALSE;
599        private String domain = null;
600        private Formula formula;
601        private Double worstValue;
602        private Double bestValue;
603        private boolean optimizedBestValue = false;
604        private boolean hidden = false;
605        private boolean userManaged = false;
606    
607        /**
608         * @param key the metric key, should be unique among all metrics
609         * @param name the metric name
610         * @param type the metric type
611         */
612        public Builder(String key, String name, ValueType type) {
613          if (StringUtils.isBlank(key)) {
614            throw new IllegalArgumentException("Metric key can not be blank");
615          }
616          if (StringUtils.isBlank(name)) {
617            throw new IllegalArgumentException("Metric name can not be blank");
618          }
619          if (type == null) {
620            throw new IllegalArgumentException("Metric type can not be null");
621          }
622          this.key = key;
623          this.name = name;
624          this.type = type;
625        }
626    
627        /**
628         * Sets the metric description.
629         */
630        public Builder setDescription(String s) {
631          this.description = s;
632          return this;
633        }
634    
635        /**
636         * Sets the metric direction. Used for numeric values only.
637         *
638         * @see Metric#DIRECTION_WORST
639         * @see Metric#DIRECTION_BETTER
640         * @see Metric#DIRECTION_NONE
641         */
642        public Builder setDirection(Integer i) {
643          this.direction = i;
644          return this;
645        }
646    
647        /**
648         * Sets whether the metric is qualitative.
649         */
650        public Builder setQualitative(Boolean b) {
651          this.qualitative = b;
652          return this;
653        }
654    
655        /**
656         * Sets the domain for the metric (General, Complexity...).
657         */
658        public Builder setDomain(String s) {
659          this.domain = s;
660          return this;
661        }
662    
663        public Builder setFormula(Formula f) {
664          this.formula = f;
665          return this;
666        }
667    
668        /**
669         * Sets the worst value.
670         */
671        public Builder setWorstValue(Double d) {
672          this.worstValue = d;
673          return this;
674        }
675    
676        /**
677         * Sets the best value. Resources would be hidden on drilldown page, if value of measure equal to best value.
678         */
679        public Builder setBestValue(Double d) {
680          this.bestValue = d;
681          return this;
682        }
683    
684        public Builder setOptimizedBestValue(boolean b) {
685          this.optimizedBestValue = b;
686          return this;
687        }
688    
689        /**
690         * Sets whether the metric should be hidden in UI (e.g. in Time Machine).
691         */
692        public Builder setHidden(boolean b) {
693          this.hidden = b;
694          return this;
695        }
696    
697        /**
698         * Values of user-managed metrics can be set online in the "Manual measures" page.
699         * @since 2.10
700         */
701        public boolean isUserManaged() {
702          return userManaged;
703        }
704    
705        /**
706         * Values of user-managed metrics can be set online in the "Manual measures" page.
707         * 
708         * @since 2.10
709         */
710        public Builder setUserManaged(boolean b) {
711          this.userManaged = b;
712          return this;
713        }
714    
715        public Metric create() {
716          if (ValueType.PERCENT.equals(this.type)) {
717            this.bestValue = (direction == DIRECTION_BETTER ? 100.0 : 0.0);
718            this.worstValue = (direction == DIRECTION_BETTER ? 0.0 : 100.0);
719          }
720          return new Metric(key, name, type, description, direction, domain, qualitative, worstValue, bestValue, optimizedBestValue, hidden, formula, userManaged);
721        }
722      }
723    }