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 public void render()
288 throws IOException
289 {
290 processViolations();
291 }
292
293 public void endDocument()
294 throws IOException
295 {
296 if ( fileCount == 0 )
297 {
298 sink.paragraph();
299 sink.text( bundle.getString( "report.pmd.noProblems" ) );
300 sink.paragraph_();
301 }
302
303 sink.section1_();
304
305
306
307
308
309
310
311
312 sink.body_();
313
314 sink.flush();
315
316 sink.close();
317 }
318
319 public void setFiles( Map<File, PmdFileInfo> files )
320 {
321 this.files = files;
322 }
323 }