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