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