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