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