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