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 }