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