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 > 0 || 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 < 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 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 < 0 ? -index - 1 : 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 }
|