View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd;
    5
    6   import java.io.File;
    7   import java.util.ArrayList;
    8   import java.util.Collections;
    9   import java.util.HashMap;
    10  import java.util.HashSet;
    11  import java.util.Iterator;
    12  import java.util.List;
    13  import java.util.Map;
    14  import java.util.Set;
    15
    16  import net.sourceforge.pmd.lang.dfa.report.ReportTree;
    17  import net.sourceforge.pmd.stat.Metric;
    18  import net.sourceforge.pmd.util.DateTimeUtil;
    19  import net.sourceforge.pmd.util.EmptyIterator;
    20  import net.sourceforge.pmd.util.NumericConstants;
    21  import net.sourceforge.pmd.util.StringUtil;
    22
    23  public class Report {
    24
    25      public static Report createReport(RuleContext ctx, String fileName) {
    26  	Report report = new Report();
    27
    28  	// overtake the listener
    29  	report.addSynchronizedListeners(ctx.getReport().getSynchronizedListeners());
    30
    31  	ctx.setReport(report);
    32  	ctx.setSourceCodeFilename(fileName);
    33  	ctx.setSourceCodeFile(new File(fileName));
    34  	return report;
    35      }
    36
    37  	public static class ReadableDuration {
    38          private final long duration;
    39
    40          public ReadableDuration(long duration) {
    41              this.duration = duration;
    42          }
    43
    44          public String getTime() {
    45              return DateTimeUtil.asHoursMinutesSeconds(duration);
    46          }
    47      }
    48
    49      public static class RuleConfigurationError {
    50          private final Rule rule;
    51          private final String issue;
    52
    53          public RuleConfigurationError(Rule theRule, String theIssue) {
    54              rule = theRule;
    55              issue = theIssue;
    56          }
    57
    58          public Rule rule() { return rule;  }
    59          public String issue() { return issue; }
    60      }
    61
    62      public static class ProcessingError {
    63          private final String msg;
    64          private final String file;
    65
    66          public ProcessingError(String msg, String file) {
    67              this.msg = msg;
    68              this.file = file;
    69          }
    70
    71          public String getMsg() {
    72              return msg;
    73          }
    74
    75          public String getFile() {
    76              return file;
    77          }
    78      }
    79
    80      public static class SuppressedViolation {
    81          private final RuleViolation rv;
    82          private final boolean isNOPMD;
    83          private final String userMessage;
    84
    85          public SuppressedViolation(RuleViolation rv, boolean isNOPMD, String userMessage) {
    86              this.isNOPMD = isNOPMD;
    87              this.rv = rv;
    88              this.userMessage = userMessage;
    89          }
    90
    91          public boolean suppressedByNOPMD() {
    92              return this.isNOPMD;
    93          }
    94
    95          public boolean suppressedByAnnotation() {
    96              return !this.isNOPMD;
    97          }
    98
    99          public RuleViolation getRuleViolation() {
    100             return this.rv;
    101         }
    102
    103         public String getUserMessage() {
    104             return userMessage;
    105         }
    106     }
    107
    108     /*
    109      * The idea is to store the violations in a tree instead of a list, to do
    110      * better and faster sort and filter mechanism and to visualize the result
    111      * as tree. (ide plugins).
    112      */
    113     private final ReportTree violationTree = new ReportTree();
    114
    115     // Note that this and the above data structure are both being maintained for a bit
    116     private final List<RuleViolation> violations = new ArrayList<RuleViolation>();
    117     private final Set<Metric> metrics = new HashSet<Metric>();
    118     private final List<SynchronizedReportListener> listeners = new ArrayList<SynchronizedReportListener>();
    119     private List<ProcessingError> errors;
    120     private List<RuleConfigurationError> configErrors;
    121     private Map<Integer, String> linesToSuppress = new HashMap<Integer, String>();
    122     private long start;
    123     private long end;
    124
    125     private List<SuppressedViolation> suppressedRuleViolations = new ArrayList<SuppressedViolation>();
    126
    127     public void suppress(Map<Integer, String> lines) {
    128         linesToSuppress = lines;
    129     }
    130
    131     private static String keyFor(RuleViolation rv) {
    132
    133     	return StringUtil.isNotEmpty(rv.getPackageName()) ?
    134             rv.getPackageName() + '.' + rv.getClassName() :
    135         	"";
    136     }
    137
    138     public Map<String, Integer> getCountSummary() {
    139         Map<String, Integer> summary = new HashMap<String, Integer>();
    140         for (Iterator<RuleViolation> iter = violationTree.iterator(); iter.hasNext();) {
    141             RuleViolation rv = iter.next();
    142             String key = keyFor(rv);
    143             Integer o = summary.get(key);
    144             summary.put(key, o==null ? NumericConstants.ONE : o+1);
    145         }
    146         return summary;
    147     }
    148
    149     public ReportTree getViolationTree() {
    150         return this.violationTree;
    151     }
    152
    153     /**
    154      * @return a Map summarizing the Report: String (rule name) ->Integer (count of violations)
    155      */
    156     public Map<String, Integer> getSummary() {
    157         Map<String, Integer> summary = new HashMap<String, Integer>();
    158         for (RuleViolation rv: violations) {
    159             String name = rv.getRule().getName();
    160             if (!summary.containsKey(name)) {
    161                 summary.put(name, NumericConstants.ZERO);
    162             }
    163             Integer count = summary.get(name);
    164             summary.put(name, count + 1);
    165         }
    166         return summary;
    167     }
    168
    169     public void addListener(ReportListener listener) {
    170         listeners.add(new SynchronizedReportListener(listener));
    171     }
    172
    173     public List<SuppressedViolation> getSuppressedRuleViolations() {
    174         return suppressedRuleViolations;
    175     }
    176
    177     public void addRuleViolation(RuleViolation violation) {
    178
    179         // NOPMD suppress
    180         int line = violation.getBeginLine();
    181         if (linesToSuppress.containsKey(line)) {
    182             suppressedRuleViolations.add(new SuppressedViolation(violation, true, linesToSuppress.get(line)));
    183             return;
    184         }
    185
    186         if (violation.isSuppressed()) {
    187             suppressedRuleViolations.add(new SuppressedViolation(violation, false, null));
    188             return;
    189         }
    190
    191
    192         int index = Collections.binarySearch(violations, violation, RuleViolationComparator.INSTANCE);
    193         violations.add(index < 0 ? -index - 1 : index, violation);
    194         violationTree.addRuleViolation(violation);
    195         for (ReportListener listener: listeners) {
    196             listener.ruleViolationAdded(violation);
    197         }
    198     }
    199
    200     public void addMetric(Metric metric) {
    201         metrics.add(metric);
    202         for (ReportListener listener: listeners) {
    203             listener.metricAdded(metric);
    204         }
    205     }
    206
    207     public void addConfigError(RuleConfigurationError error) {
    208     	if (configErrors == null) configErrors = new ArrayList<RuleConfigurationError>();
    209     	configErrors.add(error);
    210     }
    211
    212     public void addError(ProcessingError error) {
    213     	if (errors == null) errors = new ArrayList<ProcessingError>();
    214         errors.add(error);
    215     }
    216
    217     public void merge(Report r) {
    218         Iterator<ProcessingError> i = r.errors();
    219         while (i.hasNext()) {
    220             addError(i.next());
    221         }
    222         Iterator<Metric> m = r.metrics();
    223         while (m.hasNext()) {
    224             addMetric(m.next());
    225         }
    226         Iterator<RuleViolation> v = r.iterator();
    227         while (v.hasNext()) {
    228             RuleViolation violation = v.next();
    229             int index = Collections.binarySearch(violations, violation, RuleViolationComparator.INSTANCE);
    230             violations.add(index < 0 ? -index - 1 : index, violation);
    231             violationTree.addRuleViolation(violation);
    232         }
    233         Iterator<SuppressedViolation> s = r.getSuppressedRuleViolations().iterator();
    234         while (s.hasNext()) {
    235             suppressedRuleViolations.add(s.next());
    236         }
    237     }
    238
    239     public boolean hasMetrics() {
    240         return !metrics.isEmpty();
    241     }
    242
    243     public Iterator<Metric> metrics() {
    244         return metrics.iterator();
    245     }
    246
    247     public boolean isEmpty() {
    248         return !violations.iterator().hasNext() && !hasErrors();
    249     }
    250
    251     public boolean hasErrors() {
    252     	return errors != null;
    253     }
    254
    255     public boolean hasConfigErrors() {
    256     	return configErrors != null;
    257     }
    258
    259     public boolean treeIsEmpty() {
    260         return !violationTree.iterator().hasNext();
    261     }
    262
    263     public Iterator<RuleViolation> treeIterator() {
    264         return violationTree.iterator();
    265     }
    266
    267     public Iterator<RuleViolation> iterator() {
    268         return violations.iterator();
    269     }
    270
    271     public Iterator<ProcessingError> errors() {
    272         return errors == null ? EmptyIterator.<ProcessingError> instance() : errors.iterator();
    273     }
    274
    275     public Iterator<RuleConfigurationError> configErrors() {
    276         return configErrors == null ? EmptyIterator.<RuleConfigurationError> instance() : configErrors.iterator();
    277     }
    278
    279     public int treeSize() {
    280         return violationTree.size();
    281     }
    282
    283     public int size() {
    284         return violations.size();
    285     }
    286
    287     public void start() {
    288         start = System.currentTimeMillis();
    289     }
    290
    291     public void end() {
    292         end = System.currentTimeMillis();
    293     }
    294
    295     public long getElapsedTimeInMillis() {
    296         return end - start;
    297     }
    298
    299     public List<SynchronizedReportListener> getSynchronizedListeners() {
    300 	return listeners;
    301     }
    302
    303     public void addSynchronizedListeners(List<SynchronizedReportListener> synchronizedListeners) {
    304 	listeners.addAll(synchronizedListeners);
    305     }
    306 }