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;
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")) {
61 deviation = getStdDev();
62 double sigma = getDoubleProperty(sigmaDescriptor);
63 minimum = getMean() + (sigma * deviation);
64 }
65
66 if (hasProperty("minimum")) {
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")) {
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 }