aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java
blob: 5d21668d08352eec4fd0e709d14c981f254d2bc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * Sonar, open source software quality management tool.
 * Copyright (C) 2008-2011 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * Sonar is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.api.measures;

import org.apache.commons.collections.SortedBag;
import org.apache.commons.collections.bag.TreeBag;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.api.utils.SonarException;

import java.util.Map;

/**
 * Utility to build a distribution based on discrete values
 *
 * <p>An example of usage : you wish to record the number of violations for each level of rules priority</p>
 *
 * @since 1.10
 */
public class CountDistributionBuilder implements MeasureBuilder {

  private Metric metric;
  private SortedBag countBag;

  /**
   * Creates an empty CountDistributionBuilder for a specified metric
   *
   * @param metric the metric
   */
  public CountDistributionBuilder(Metric metric) {
    setMetric(metric);
    this.countBag = new TreeBag();
  }

  /**
   * Increments an entry
   *
   * @param value the value that should be incremented
   * @param count the number by which to increment
   * @return the current object
   */
  public CountDistributionBuilder add(Object value, int count) {
    if (count == 0) {
      addZero(value);

    } else {
      if (this.countBag.add(value, count)) {
        this.countBag.add(value, 1);//hack
      }
    }
    return this;
  }

  /**
   * Increments an entry by one
   *
   * @param value the value that should be incremented
   * @return the current object
   */
  public CountDistributionBuilder add(Object value) {
    return add(value, 1);
  }

  /**
   * Adds an entry without a zero count if it does not exist
   *
   * @param value the entry to be added
   * @return the current object
   */
  public CountDistributionBuilder addZero(Object value) {
    if (!countBag.contains(value)) {
      countBag.add(value, 1);
    }
    return this;
  }

  /**
   * Adds an existing Distribution to the current one.
   * It will create the entries if they don't exist.
   * Can be used to add the values of children resources for example
   *
   * @param measure the measure to add to the current one
   * @return the current object
   */
  public CountDistributionBuilder add(Measure measure) {
    if (measure != null && measure.getData() != null) {
      Map<String, String> map = KeyValueFormat.parse(measure.getData());
      for (Map.Entry<String, String> entry : map.entrySet()) {
        String key = entry.getKey();
        int value = (StringUtils.isBlank(entry.getValue()) ? 0 : Integer.parseInt(entry.getValue()));
        if (NumberUtils.isNumber(key)) {
          add(NumberUtils.toInt(key), value);
        } else {
          add(key, value);
        }
      }
    }
    return this;
  }

  /**
   * @return whether the current object is empty or not
   */
  public boolean isEmpty() {
    return countBag.isEmpty();
  }

  /**
   * Resets all entries to zero
   *
   * @return the current object
   */
  public CountDistributionBuilder clear() {
    countBag.clear();
    return this;
  }

  /**
   * Shortcut for <code>build(true)</code>
   *
   * @return the built measure
   */
  public Measure build() {
    return build(true);
  }

  /**
   * Used to build a measure from the current object
   *
   * @param allowEmptyData should be built if current object is empty
   * @return the built measure
   */
  public Measure build(boolean allowEmptyData) {
    if (!isEmpty() || allowEmptyData) {
      return new Measure(metric, KeyValueFormat.format(countBag, -1)); //-1 is a hack to include zero values
    }
    return null;
  }

  private void setMetric(Metric metric) {
    if (metric == null || !metric.isDataType()) {
      throw new SonarException("Metric is null or has unvalid type");
    }
    this.metric = metric;
  }
}