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