1 package org.apache.maven.plugin.pmd;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.ResourceBundle;
32
33 import net.sourceforge.pmd.RuleViolation;
34
35 import org.apache.maven.doxia.sink.Sink;
36 import org.apache.maven.plugin.logging.Log;
37 import org.codehaus.plexus.util.StringUtils;
38
39
40
41
42
43
44
45 public class PmdReportGenerator
46 {
47 private Log log;
48
49 private Sink sink;
50
51 private String currentFilename;
52
53 private ResourceBundle bundle;
54
55 private HashSet<RuleViolation> violations = new HashSet<RuleViolation>();
56
57 private boolean aggregate;
58
59
60 private int fileCount = 0;
61
62 private Map<File, PmdFileInfo> files;
63
64
65
66 public PmdReportGenerator( Log log, Sink sink, ResourceBundle bundle, boolean aggregate )
67 {
68 this.log = log;
69 this.sink = sink;
70 this.bundle = bundle;
71 this.aggregate = aggregate;
72 }
73
74 private String getTitle()
75 {
76 return bundle.getString( "report.pmd.title" );
77 }
78
79 public void setViolations( Collection<RuleViolation> violations )
80 {
81 this.violations = new HashSet<RuleViolation>( violations );
82 }
83
84 public List<RuleViolation> getViolations()
85 {
86 return new ArrayList<RuleViolation>( violations );
87 }
88
89
90
91
92
93
94
95
96
97
98
99 private void startFileSection( String currentFilename, PmdFileInfo fileInfo )
100 {
101 sink.section2();
102 sink.sectionTitle2();
103
104
105 this.currentFilename = currentFilename;
106 if ( fileInfo != null && fileInfo.getSourceDirectory() != null )
107 {
108 this.currentFilename =
109 StringUtils.substring( currentFilename, fileInfo.getSourceDirectory().getAbsolutePath().length() + 1 );
110 }
111 this.currentFilename = StringUtils.replace( this.currentFilename, "\\", "/" );
112
113 String title = this.currentFilename;
114 if ( aggregate && fileInfo != null && fileInfo.getProject() != null )
115 {
116 title = fileInfo.getProject().getName() + " - " + this.currentFilename;
117 }
118 sink.text( title );
119 sink.sectionTitle2_();
120
121 sink.table();
122 sink.tableRow();
123 sink.tableHeaderCell();
124 sink.text( bundle.getString( "report.pmd.column.violation" ) );
125 sink.tableHeaderCell_();
126 sink.tableHeaderCell();
127 sink.text( bundle.getString( "report.pmd.column.line" ) );
128 sink.tableHeaderCell_();
129 sink.tableRow_();
130 }
131
132 private void endFileSection()
133 {
134 sink.table_();
135 sink.section2_();
136 }
137
138 private void processSingleRuleViolation( RuleViolation ruleViolation, PmdFileInfo fileInfo )
139 {
140 sink.tableRow();
141 sink.tableCell();
142 sink.text( ruleViolation.getDescription() );
143 sink.tableCell_();
144 sink.tableCell();
145
146 int beginLine = ruleViolation.getBeginLine();
147 outputLineLink( beginLine, fileInfo );
148 int endLine = ruleViolation.getEndLine();
149 if ( endLine != beginLine )
150 {
151 sink.text( "–" );
152 outputLineLink( endLine, fileInfo );
153 }
154
155 sink.tableCell_();
156 sink.tableRow_();
157 }
158
159
160
161
162 private void processViolations()
163 throws IOException
164 {
165 fileCount = files.size();
166 ArrayList<RuleViolation> violations2 = new ArrayList<RuleViolation>( violations );
167 Collections.sort( violations2, new Comparator<RuleViolation>()
168 {
169
170 public int compare( RuleViolation o1, RuleViolation o2 )
171 {
172 int filenames = o1.getFilename().compareTo( o2.getFilename() );
173 if ( filenames == 0 )
174 {
175 return o1.getBeginLine() - o2.getBeginLine();
176 }
177 else
178 {
179 return filenames;
180 }
181 }
182 } );
183
184 boolean fileSectionStarted = false;
185 String previousFilename = null;
186 for ( RuleViolation ruleViolation : violations2 )
187 {
188 String currentFn = ruleViolation.getFilename();
189 File canonicalFilename = new File( currentFn ).getCanonicalFile();
190 PmdFileInfo fileInfo = files.get( canonicalFilename );
191 if ( fileInfo == null )
192 {
193 log.warn( "Couldn't determine PmdFileInfo for file " + currentFn + " (canonical: " + canonicalFilename
194 + "). XRef links won't be available." );
195 }
196
197 if ( !currentFn.equalsIgnoreCase( previousFilename ) && fileSectionStarted )
198 {
199 endFileSection();
200 fileSectionStarted = false;
201 }
202 if ( !fileSectionStarted )
203 {
204 startFileSection( currentFn, fileInfo );
205 fileSectionStarted = true;
206 }
207
208 processSingleRuleViolation( ruleViolation, fileInfo );
209
210 previousFilename = currentFn;
211 }
212
213 if ( fileSectionStarted )
214 {
215 endFileSection();
216 }
217 }
218
219 private void outputLineLink( int line, PmdFileInfo fileInfo )
220 {
221 String xrefLocation = null;
222 if ( fileInfo != null )
223 {
224 xrefLocation = fileInfo.getXrefLocation();
225 }
226
227 if ( xrefLocation != null )
228 {
229 sink.link( xrefLocation + "/" + currentFilename.replaceAll( "\\.java$", ".html" ) + "#L" + line );
230 }
231 sink.text( String.valueOf( line ) );
232 if ( xrefLocation != null )
233 {
234 sink.link_();
235 }
236 }
237
238 public void beginDocument()
239 {
240 sink.head();
241 sink.title();
242 sink.text( getTitle() );
243 sink.title_();
244 sink.head_();
245
246 sink.body();
247
248 sink.section1();
249 sink.sectionTitle1();
250 sink.text( getTitle() );
251 sink.sectionTitle1_();
252
253 sink.paragraph();
254 sink.text( bundle.getString( "report.pmd.pmdlink" ) + " " );
255 sink.link( "http://pmd.sourceforge.net/" );
256 sink.text( "PMD" );
257 sink.link_();
258 sink.text( " " + AbstractPmdReport.getPmdVersion() + "." );
259 sink.paragraph_();
260
261 sink.section1_();
262
263
264
265 sink.section1();
266 sink.sectionTitle1();
267 sink.text( bundle.getString( "report.pmd.files" ) );
268 sink.sectionTitle1_();
269
270
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 public void render()
331 throws IOException
332 {
333 processViolations();
334 }
335
336 public void endDocument()
337 throws IOException
338 {
339 if ( fileCount == 0 )
340 {
341 sink.paragraph();
342 sink.text( bundle.getString( "report.pmd.noProblems" ) );
343 sink.paragraph_();
344 }
345
346 sink.section1_();
347
348
349
350
351
352
353
354
355 sink.body_();
356
357 sink.flush();
358
359 sink.close();
360 }
361
362 public void setFiles( Map<File, PmdFileInfo> files )
363 {
364 this.files = files;
365 }
366 }