001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.lang.rule.stat;
005
006 import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.MINIMUM_DESCRIPTOR;
007 import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.SIGMA_DESCRIPTOR;
008 import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.TOP_SCORE_DESCRIPTOR;
009
010 import java.util.Set;
011 import java.util.SortedSet;
012 import java.util.TreeSet;
013
014 import net.sourceforge.pmd.RuleContext;
015 import net.sourceforge.pmd.lang.rule.AbstractRule;
016 import net.sourceforge.pmd.stat.DataPoint;
017 import net.sourceforge.pmd.stat.Metric;
018
019 /**
020 * This class is used to implement the core logic of a StatisticalRule.
021 * Concrete Rule implementations should delegate to an instance of this class.
022 *
023 * @author David Dixon-Peugh
024 * Aug 8, 2002 StatisticalRule.java
025 */
026 public class StatisticalRuleHelper {
027
028 public static final double DELTA = 0.000005; // Within this range. . .
029
030 private AbstractRule rule;
031
032 private SortedSet<DataPoint> dataPoints = new TreeSet<DataPoint>();
033
034 private int count = 0;
035 private double total = 0.0;
036
037 public StatisticalRuleHelper(AbstractRule rule) {
038 this.rule = rule;
039 rule.definePropertyDescriptor(SIGMA_DESCRIPTOR);
040 rule.definePropertyDescriptor(MINIMUM_DESCRIPTOR);
041 rule.definePropertyDescriptor(TOP_SCORE_DESCRIPTOR);
042 }
043
044 public void addDataPoint(DataPoint point) {
045 count++;
046 total += point.getScore();
047 dataPoints.add(point);
048 }
049
050 public void apply(RuleContext ctx) {
051
052 double deviation;
053 double minimum = 0.0;
054
055 if (rule.getProperty(SIGMA_DESCRIPTOR) != null) { // TODO - need to come up with a good default value
056 deviation = getStdDev();
057 double sigma = rule.getProperty(SIGMA_DESCRIPTOR);
058 minimum = getMean() + (sigma * deviation);
059 }
060
061 if (rule.getProperty(MINIMUM_DESCRIPTOR) != null) { // TODO - need to come up with a good default value
062 double mMin = rule.getProperty(MINIMUM_DESCRIPTOR);
063 if (mMin > minimum) {
064 minimum = mMin;
065 }
066 }
067
068 SortedSet<DataPoint> newPoints = applyMinimumValue(dataPoints, minimum);
069
070 if (rule.getProperty(TOP_SCORE_DESCRIPTOR) != null) { // TODO - need to come up with a good default value
071 int topScore = rule.getProperty(TOP_SCORE_DESCRIPTOR);
072 if (newPoints.size() >= topScore) {
073 newPoints = applyTopScore(newPoints, topScore);
074 }
075 }
076
077 makeViolations(ctx, newPoints);
078
079 double low = 0.0d;
080 double high = 0.0d;
081 if (!dataPoints.isEmpty()) {
082 low = dataPoints.first().getScore();
083 high = dataPoints.last().getScore();
084 }
085
086 ctx.getReport().addMetric(new Metric(rule.getName(), count, total, low, high, getMean(), getStdDev()));
087
088 dataPoints.clear();
089 }
090
091 private double getMean() {
092 return total / count;
093 }
094
095 private double getStdDev() {
096 if (dataPoints.size() < 2) {
097 return Double.NaN;
098 }
099
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 }
|