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