Report.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd;
005 
006 import java.util.ArrayList;
007 import java.util.Collections;
008 import java.util.HashMap;
009 import java.util.HashSet;
010 import java.util.Iterator;
011 import java.util.List;
012 import java.util.Map;
013 import java.util.Set;
014 
015 import net.sourceforge.pmd.lang.dfa.report.ReportTree;
016 import net.sourceforge.pmd.stat.Metric;
017 import net.sourceforge.pmd.util.NumericConstants;
018 
019 public class Report {
020 
021     public static class ReadableDuration {
022         private final long duration;
023 
024         public ReadableDuration(long duration) {
025             this.duration = duration;
026         }
027 
028         public String getTime() {
029             long seconds = 0;
030             long minutes = 0;
031             long hours = 0;
032 
033             if (duration > 1000) {
034                 seconds = duration / 1000;
035             }
036 
037             if (seconds > 60) {
038                 minutes = seconds / 60;
039                 seconds = seconds % 60;
040             }
041 
042             if (minutes > 60) {
043                 hours = minutes / 60;
044                 minutes = minutes % 60;
045             }
046 
047             StringBuffer res = new StringBuffer();
048             if (hours > 0) {
049                 res.append(hours).append("h ");
050             }
051             if (hours > || minutes > 0) {
052                 res.append(minutes).append("m ");
053             }
054             res.append(seconds).append('s');
055             return res.toString();
056         }
057     }
058 
059     public static class ProcessingError {
060         private final String msg;
061         private final String file;
062 
063         public ProcessingError(String msg, String file) {
064             this.msg = msg;
065             this.file = file;
066         }
067 
068         public String getMsg() {
069             return msg;
070         }
071 
072         public String getFile() {
073             return file;
074         }
075     }
076 
077     public static class SuppressedViolation {
078         private final RuleViolation rv;
079         private final boolean isNOPMD;
080         private final String userMessage;
081 
082         public SuppressedViolation(RuleViolation rv, boolean isNOPMD, String userMessage) {
083             this.isNOPMD = isNOPMD;
084             this.rv = rv;
085             this.userMessage = userMessage;
086         }
087 
088         public boolean suppressedByNOPMD() {
089             return this.isNOPMD;
090         }
091 
092         public boolean suppressedByAnnotation() {
093             return !this.isNOPMD;
094         }
095 
096         public RuleViolation getRuleViolation() {
097             return this.rv;
098         }
099 
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 final List<ProcessingError> errors = new ArrayList<ProcessingError>();
117     private Map<Integer, String> linesToSuppress = new HashMap<Integer, String>();
118     private long start;
119     private long end;
120 
121     private List<SuppressedViolation> suppressedRuleViolations = new ArrayList<SuppressedViolation>();
122 
123     public void suppress(Map<Integer, String> lines) {
124         linesToSuppress = lines;
125     }
126 
127     public Map<String, Integer> getCountSummary() {
128         Map<String, Integer> summary = new HashMap<String, Integer>();
129         for (Iterator<RuleViolation> iter = violationTree.iterator(); iter.hasNext();) {
130             RuleViolation rv = iter.next();
131             String key = "";
132             if (rv.getPackageName() != null && rv.getPackageName().length() != 0) {
133                 key = rv.getPackageName() '.' + rv.getClassName();
134             }
135             Integer o = summary.get(key);
136             if (o == null) {
137                 summary.put(key, NumericConstants.ONE);
138             else {
139                 summary.put(key, o+1);
140             }
141         }
142         return summary;
143     }
144 
145     public ReportTree getViolationTree() {
146         return this.violationTree;
147     }
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 < ? -index - : 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 addError(ProcessingError error) {
205         errors.add(error);
206     }
207 
208     public void merge(Report r) {
209         Iterator<ProcessingError> i = r.errors();
210         while (i.hasNext()) {
211             addError(i.next());
212         }
213         Iterator<Metric> m = r.metrics();
214         while (m.hasNext()) {
215             addMetric(m.next());
216         }
217         Iterator<RuleViolation> v = r.iterator();
218         while (v.hasNext()) {
219             RuleViolation violation = v.next();
220             int index = Collections.binarySearch(violations, violation, RuleViolationComparator.INSTANCE);
221             violations.add(index < ? -index - : index, violation);
222             violationTree.addRuleViolation(violation);
223         }
224         Iterator<SuppressedViolation> s = r.getSuppressedRuleViolations().iterator();
225         while (s.hasNext()) {
226             suppressedRuleViolations.add(s.next());
227         }
228     }
229 
230     public boolean hasMetrics() {
231         return !metrics.isEmpty();
232     }
233 
234     public Iterator<Metric> metrics() {
235         return metrics.iterator();
236     }
237 
238     public boolean isEmpty() {
239         return !violations.iterator().hasNext() && errors.isEmpty();
240     }
241 
242     public boolean treeIsEmpty() {
243         return !violationTree.iterator().hasNext();
244     }
245 
246     public Iterator<RuleViolation> treeIterator() {
247         return violationTree.iterator();
248     }
249 
250     public Iterator<RuleViolation> iterator() {
251         return violations.iterator();
252     }
253 
254     public Iterator<ProcessingError> errors() {
255         return errors.iterator();
256     }
257 
258     public int treeSize() {
259         return violationTree.size();
260     }
261 
262     public int size() {
263         return violations.size();
264     }
265 
266     public void start() {
267         start = System.currentTimeMillis();
268     }
269 
270     public void end() {
271         end = System.currentTimeMillis();
272     }
273 
274     public long getElapsedTimeInMillis() {
275         return end - start;
276     }
277 }