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