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 }