View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.rule.stat;
5   
6   import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.MINIMUM_DESCRIPTOR;
7   import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.SIGMA_DESCRIPTOR;
8   import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.TOP_SCORE_DESCRIPTOR;
9   
10  import java.util.Set;
11  import java.util.SortedSet;
12  import java.util.TreeSet;
13  
14  import net.sourceforge.pmd.RuleContext;
15  import net.sourceforge.pmd.lang.rule.AbstractRule;
16  import net.sourceforge.pmd.stat.DataPoint;
17  import net.sourceforge.pmd.stat.Metric;
18  
19  /**
20   * This class is used to implement the core logic of a StatisticalRule.
21   * Concrete Rule implementations should delegate to an instance of this class.
22   * 
23   * @author David Dixon-Peugh
24   *         Aug 8, 2002 StatisticalRule.java
25   */
26  public class StatisticalRuleHelper {
27  
28      public static final double DELTA = 0.000005; // Within this range. . .
29      
30      private AbstractRule rule;
31  
32      private SortedSet<DataPoint> dataPoints = new TreeSet<DataPoint>();
33  
34      private int count = 0;
35      private double total = 0.0;
36  
37      public StatisticalRuleHelper(AbstractRule rule) {
38      	this.rule = rule;
39      	rule.definePropertyDescriptor(SIGMA_DESCRIPTOR);
40      	rule.definePropertyDescriptor(MINIMUM_DESCRIPTOR);
41      	rule.definePropertyDescriptor(TOP_SCORE_DESCRIPTOR);
42      }
43  
44      public void addDataPoint(DataPoint point) {
45          count++;
46          total += point.getScore();
47          dataPoints.add(point);
48      }
49  
50      public void apply(RuleContext ctx) {
51  
52          double deviation;
53          double minimum = 0.0;
54  
55          if (rule.getProperty(SIGMA_DESCRIPTOR) != null) {	// TODO - need to come up with a good default value
56              deviation = getStdDev();
57              double sigma = rule.getProperty(SIGMA_DESCRIPTOR);
58              minimum = getMean() + (sigma * deviation);
59          }
60  
61          if (rule.getProperty(MINIMUM_DESCRIPTOR) != null) {	// TODO - need to come up with a good default value
62              double mMin = rule.getProperty(MINIMUM_DESCRIPTOR);
63              if (mMin > minimum) {
64                  minimum = mMin;
65              }
66          }
67  
68          SortedSet<DataPoint> newPoints = applyMinimumValue(dataPoints, minimum);
69  
70          if (rule.getProperty(TOP_SCORE_DESCRIPTOR) != null) {	// TODO - need to come up with a good default value
71              int topScore = rule.getProperty(TOP_SCORE_DESCRIPTOR);
72              if (newPoints.size() >= topScore) {
73                  newPoints = applyTopScore(newPoints, topScore);
74              }
75          }
76  
77          makeViolations(ctx, newPoints);
78  
79          double low = 0.0d;
80          double high = 0.0d;
81          if (!dataPoints.isEmpty()) {
82              low = dataPoints.first().getScore();
83              high = dataPoints.last().getScore();
84          }
85  
86          ctx.getReport().addMetric(new Metric(rule.getName(), count, total, low, high, getMean(), getStdDev()));
87  
88          dataPoints.clear();
89      }
90  
91      private double getMean() {
92          return total / count;
93      }
94  
95      private double getStdDev() {
96          if (dataPoints.size() < 2) {
97              return Double.NaN;
98          }
99  
100         double mean = getMean();
101         double deltaSq = 0.0;
102         double scoreMinusMean;
103 
104         for (DataPoint point: dataPoints) {
105             scoreMinusMean = point.getScore() - mean;
106             deltaSq += scoreMinusMean * scoreMinusMean;
107         }
108 
109         return Math.sqrt(deltaSq / (dataPoints.size() - 1));
110     }
111 
112     private SortedSet<DataPoint> applyMinimumValue(SortedSet<DataPoint> pointSet, double minValue) {
113         SortedSet<DataPoint> rc = new TreeSet<DataPoint>();
114         double threshold = minValue - DELTA;
115 
116         for (DataPoint point: pointSet) {
117             if (point.getScore() > threshold) {
118                 rc.add(point);
119             }
120         }
121         return rc;
122     }
123 
124     private SortedSet<DataPoint> applyTopScore(SortedSet<DataPoint> points, int topScore) {
125         SortedSet<DataPoint> s = new TreeSet<DataPoint>();
126         DataPoint[] arr = points.toArray(new DataPoint[]{});
127         for (int i = arr.length - 1; i >= (arr.length - topScore); i--) {
128             s.add(arr[i]);
129         }
130         return s;
131     }
132 
133     private void makeViolations(RuleContext ctx, Set<DataPoint> p) {
134         for (DataPoint point: p) {
135             rule.addViolationWithMessage(ctx, point.getNode(), point.getMessage(), ((StatisticalRule)rule).getViolationParameters(point));
136         }
137     }
138 }