View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.stat;
5   
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Set;
9   import java.util.SortedSet;
10  import java.util.TreeSet;
11  
12  import net.sourceforge.pmd.AbstractJavaRule;
13  import net.sourceforge.pmd.PropertyDescriptor;
14  import net.sourceforge.pmd.RuleContext;
15  import net.sourceforge.pmd.properties.DoubleProperty;
16  import net.sourceforge.pmd.properties.IntegerProperty;
17  
18  /**
19   * @author David Dixon-Peugh
20   *         Aug 8, 2002 StatisticalRule.java
21   */
22  public abstract class StatisticalRule extends AbstractJavaRule {
23  
24      public static final double DELTA = 0.000005; // Within this range. . .
25  
26      private SortedSet<DataPoint> dataPoints = new TreeSet<DataPoint>();
27  
28      private int count = 0;
29      private double total = 0.0;
30  
31      private static final PropertyDescriptor sigmaDescriptor = new DoubleProperty(
32      	"sigma", "Sigma value",	0,	1.0f
33      	);
34  
35      private static final PropertyDescriptor minimumDescriptor = new DoubleProperty(
36          "minimum", "Minimum value",	0,	1.0f
37          );
38  
39      private static final PropertyDescriptor topScoreDescriptor = new IntegerProperty(
40          "topscore", "Top score value",	0,	1.0f
41          );
42  
43      private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap( new PropertyDescriptor[] {
44      	sigmaDescriptor, minimumDescriptor, topScoreDescriptor
45      	});
46  
47  
48      public void addDataPoint(DataPoint point) {
49          count++;
50          total += point.getScore();
51          dataPoints.add(point);
52      }
53  
54      public void apply(List acus, RuleContext ctx) {
55          visitAll(acus, ctx);
56  
57          double deviation;
58          double minimum = 0.0;
59  
60          if (hasProperty("sigma")) {	// TODO - need to come up with a good default value
61              deviation = getStdDev();
62              double sigma = getDoubleProperty(sigmaDescriptor);
63              minimum = getMean() + (sigma * deviation);
64          }
65  
66          if (hasProperty("minimum")) {	// TODO - need to come up with a good default value
67              double mMin = getDoubleProperty(minimumDescriptor);
68              if (mMin > minimum) {
69                  minimum = mMin;
70              }
71          }
72  
73          SortedSet<DataPoint> newPoints = applyMinimumValue(dataPoints, minimum);
74  
75          if (hasProperty("topscore")) { // TODO - need to come up with a good default value
76              int topScore = getIntProperty(topScoreDescriptor);
77              if (newPoints.size() >= topScore) {
78                  newPoints = applyTopScore(newPoints, topScore);
79              }
80          }
81  
82          makeViolations(ctx, newPoints);
83  
84          double low = 0.0d;
85          double high = 0.0d;
86          if (!dataPoints.isEmpty()) {
87              low = dataPoints.first().getScore();
88              high = dataPoints.last().getScore();
89          }
90  
91          ctx.getReport().addMetric(new Metric(this.getName(), count, total, low, high, getMean(), getStdDev()));
92  
93          dataPoints.clear();
94      }
95  
96      protected double getMean() {
97          return total / count;
98      }
99  
100     protected double getStdDev() {
101         if (dataPoints.size() < 2) {
102             return Double.NaN;
103         }
104 
105         double mean = getMean();
106         double deltaSq = 0.0;
107         double scoreMinusMean;
108 
109         for (DataPoint point: dataPoints) {
110             scoreMinusMean = point.getScore() - mean;
111             deltaSq += (scoreMinusMean * scoreMinusMean);
112         }
113 
114         return Math.sqrt(deltaSq / (dataPoints.size() - 1));
115     }
116 
117     protected SortedSet<DataPoint> applyMinimumValue(SortedSet<DataPoint> pointSet, double minValue) {
118         SortedSet<DataPoint> RC = new TreeSet<DataPoint>();
119         double threshold = minValue - DELTA;
120 
121         for (DataPoint point: pointSet) {
122             if (point.getScore() > threshold) {
123                 RC.add(point);
124             }
125         }
126         return RC;
127     }
128 
129     protected SortedSet<DataPoint> applyTopScore(SortedSet<DataPoint> points, int topScore) {
130         SortedSet<DataPoint> s = new TreeSet<DataPoint>();
131         DataPoint[] arr = points.toArray(new DataPoint[]{});
132         for (int i = arr.length - 1; i >= (arr.length - topScore); i--) {
133             s.add(arr[i]);
134         }
135         return s;
136     }
137 
138     protected void makeViolations(RuleContext ctx, Set<DataPoint> p) {
139         for (DataPoint point: p) {
140             addViolationWithMessage(ctx, point.getNode(), point.getMessage());
141         }
142     }
143 
144     protected Map<String, PropertyDescriptor> propertiesByName() {
145     	return propertyDescriptorsByName;
146     }
147 }