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