/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.stat;
import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.MINIMUM_DESCRIPTOR;
import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.SIGMA_DESCRIPTOR;
import static net.sourceforge.pmd.lang.rule.stat.StatisticalRule.TOP_SCORE_DESCRIPTOR;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.rule.AbstractRule;
import net.sourceforge.pmd.stat.DataPoint;
import net.sourceforge.pmd.stat.Metric;
/**
* This class is used to implement the core logic of a StatisticalRule. Concrete
* Rule implementations should delegate to an instance of this class.
*
* @author David Dixon-Peugh Aug 8, 2002 StatisticalRule.java
*/
public class StatisticalRuleHelper {
public static final double DELTA = 0.000005; // Within this range. . .
private AbstractRule rule;
private SortedSet<DataPoint> dataPoints = new TreeSet<>();
private int count = 0;
private double total = 0.0;
public StatisticalRuleHelper(AbstractRule rule) {
this.rule = rule;
rule.definePropertyDescriptor(SIGMA_DESCRIPTOR);
rule.definePropertyDescriptor(MINIMUM_DESCRIPTOR);
rule.definePropertyDescriptor(TOP_SCORE_DESCRIPTOR);
}
public void addDataPoint(DataPoint point) {
count++;
total += point.getScore();
dataPoints.add(point);
}
public void apply(RuleContext ctx) {
double deviation;
double minimum = 0.0;
if (rule.getProperty(SIGMA_DESCRIPTOR) != null) { // TODO - need to come
// up with a good
// default value
deviation = getStdDev();
double sigma = rule.getProperty(SIGMA_DESCRIPTOR);
minimum = getMean() + (sigma * deviation);
}
if (rule.getProperty(MINIMUM_DESCRIPTOR) != null) { // TODO - need to
// come up with a
// good default
// value
double mMin = rule.getProperty(MINIMUM_DESCRIPTOR);
if (mMin > minimum) {
minimum = mMin;
}
}
SortedSet<DataPoint> newPoints = applyMinimumValue(dataPoints, minimum);
if (rule.getProperty(TOP_SCORE_DESCRIPTOR) != null) { // TODO - need to
// come up with a
// good default
// value
int topScore = rule.getProperty(TOP_SCORE_DESCRIPTOR);
if (newPoints.size() >= topScore) {
newPoints = applyTopScore(newPoints, topScore);
}
}
makeViolations(ctx, newPoints);
double low = 0.0d;
double high = 0.0d;
if (!dataPoints.isEmpty()) {
low = dataPoints.first().getScore();
high = dataPoints.last().getScore();
}
ctx.getReport().addMetric(new Metric(rule.getName(), count, total, low, high, getMean(), getStdDev()));
dataPoints.clear();
}
private double getMean() {
return total / count;
}
private double getStdDev() {
if (dataPoints.size() < 2) {
return Double.NaN;
}
double mean = getMean();
double deltaSq = 0.0;
double scoreMinusMean;
for (DataPoint point : dataPoints) {
scoreMinusMean = point.getScore() - mean;
deltaSq += scoreMinusMean * scoreMinusMean;
}
return Math.sqrt(deltaSq / (dataPoints.size() - 1));
}
private SortedSet<DataPoint> applyMinimumValue(SortedSet<DataPoint> pointSet, double minValue) {
SortedSet<DataPoint> rc = new TreeSet<>();
double threshold = minValue - DELTA;
for (DataPoint point : pointSet) {
if (point.getScore() > threshold) {
rc.add(point);
}
}
return rc;
}
private SortedSet<DataPoint> applyTopScore(SortedSet<DataPoint> points, int topScore) {
SortedSet<DataPoint> s = new TreeSet<>();
DataPoint[] arr = points.toArray(new DataPoint[] {});
for (int i = arr.length - 1; i >= (arr.length - topScore); i--) {
s.add(arr[i]);
}
return s;
}
private void makeViolations(RuleContext ctx, Set<DataPoint> p) {
for (DataPoint point : p) {
rule.addViolationWithMessage(ctx, point.getNode(), point.getMessage(),
((StatisticalRule) rule).getViolationParameters(point));
}
}
}