StatisticalRuleHelper.java
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 }