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.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.Reader;
30 import java.io.UnsupportedEncodingException;
31 import java.io.Writer;
32 import java.util.Iterator;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.ResourceBundle;
36
37 import net.sourceforge.pmd.IRuleViolation;
38 import net.sourceforge.pmd.PMD;
39 import net.sourceforge.pmd.PMDException;
40 import net.sourceforge.pmd.Report;
41 import net.sourceforge.pmd.Rule;
42 import net.sourceforge.pmd.RuleContext;
43 import net.sourceforge.pmd.RuleSet;
44 import net.sourceforge.pmd.RuleSetFactory;
45 import net.sourceforge.pmd.SourceType;
46 import net.sourceforge.pmd.renderers.CSVRenderer;
47 import net.sourceforge.pmd.renderers.HTMLRenderer;
48 import net.sourceforge.pmd.renderers.Renderer;
49 import net.sourceforge.pmd.renderers.TextRenderer;
50 import net.sourceforge.pmd.renderers.XMLRenderer;
51
52 import org.apache.maven.doxia.sink.Sink;
53 import org.apache.maven.reporting.MavenReportException;
54 import org.codehaus.plexus.resource.ResourceManager;
55 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
56 import org.codehaus.plexus.resource.loader.FileResourceLoader;
57 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
58 import org.codehaus.plexus.util.FileUtils;
59 import org.codehaus.plexus.util.IOUtil;
60 import org.codehaus.plexus.util.ReaderFactory;
61 import org.codehaus.plexus.util.StringUtils;
62
63
64
65
66
67
68
69
70
71 public class PmdReport
72 extends AbstractPmdReport
73 {
74
75
76
77
78
79
80
81
82
83 private String targetJdk;
84
85
86
87
88
89
90
91
92 private int minimumPriority = 5;
93
94
95
96
97
98
99
100
101 private boolean skip;
102
103
104
105
106
107
108
109
110 private String[] rulesets = new String[]{"rulesets/basic.xml", "rulesets/unusedcode.xml", "rulesets/imports.xml", };
111
112
113
114
115
116
117 private ResourceManager locator;
118
119
120 public String getName( Locale locale )
121 {
122 return getBundle( locale ).getString( "report.pmd.name" );
123 }
124
125
126 public String getDescription( Locale locale )
127 {
128 return getBundle( locale ).getString( "report.pmd.description" );
129 }
130
131 public void setRulesets( String[] rules )
132 {
133 rulesets = rules;
134 }
135
136
137 public void executeReport( Locale locale )
138 throws MavenReportException
139 {
140 try
141 {
142 execute( locale );
143 }
144 finally
145 {
146 if ( getSink() != null )
147 {
148 getSink().close();
149 }
150 }
151 }
152
153 private void execute( Locale locale )
154 throws MavenReportException
155 {
156
157 locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
158 locator.addSearchPath( "url", "" );
159 locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
160
161 if ( !skip && canGenerateReport() )
162 {
163 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
164 try
165 {
166 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
167
168 Report report = generateReport( locale );
169
170 if ( !isHtml() )
171 {
172 writeNonHtml( report );
173 }
174 }
175 finally
176 {
177 Thread.currentThread().setContextClassLoader( origLoader );
178 }
179 }
180 }
181
182 private Report generateReport( Locale locale )
183 throws MavenReportException
184 {
185 Sink sink = getSink();
186
187 PMD pmd = getPMD();
188 RuleContext ruleContext = new RuleContext();
189 Report report = new Report();
190 PmdReportListener reportSink = new PmdReportListener( sink, getBundle( locale ), aggregate );
191
192 report.addListener( reportSink );
193 ruleContext.setReport( report );
194 reportSink.beginDocument();
195
196 RuleSetFactory ruleSetFactory = new RuleSetFactory();
197 ruleSetFactory.setMinimumPriority( this.minimumPriority );
198 RuleSet[] sets = new RuleSet[rulesets.length];
199 try
200 {
201 for ( int idx = 0; idx < rulesets.length; idx++ )
202 {
203 String set = rulesets[idx];
204 getLog().debug( "Preparing ruleset: " + set );
205 File ruleset = locator.getResourceAsFile( set, getLocationTemp( set ) );
206
207 if ( null == ruleset )
208 {
209 throw new MavenReportException( "Could not resolve " + set );
210 }
211
212 InputStream rulesInput = new FileInputStream( ruleset );
213 try
214 {
215 RuleSet ruleSet = ruleSetFactory.createRuleSet( rulesInput );
216 sets[idx] = ruleSet;
217
218 ruleSet.start( ruleContext );
219 }
220 finally
221 {
222 rulesInput.close();
223 }
224 }
225 }
226 catch ( IOException e )
227 {
228 throw new MavenReportException( e.getMessage(), e );
229 }
230 catch ( ResourceNotFoundException e )
231 {
232 throw new MavenReportException( e.getMessage(), e );
233 }
234 catch ( FileResourceCreationException e )
235 {
236 throw new MavenReportException( e.getMessage(), e );
237 }
238
239 Map files;
240 try
241 {
242 files = getFilesToProcess( );
243 }
244 catch ( IOException e )
245 {
246 throw new MavenReportException( "Can't get file list", e );
247 }
248
249 if ( StringUtils.isEmpty( getSourceEncoding() ) && !files.isEmpty() )
250 {
251 getLog().warn( "File encoding has not been set, using platform encoding "
252 + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
253 }
254
255 for ( Iterator i = files.entrySet().iterator(); i.hasNext(); )
256 {
257 Map.Entry entry = (Map.Entry) i.next();
258 File file = (File) entry.getKey();
259 PmdFileInfo fileInfo = (PmdFileInfo) entry.getValue();
260
261
262
263 reportSink.beginFile( file , fileInfo );
264 ruleContext.setSourceCodeFilename( file.getAbsolutePath() );
265 for ( int idx = 0; idx < rulesets.length; idx++ )
266 {
267 try
268 {
269
270
271 Reader reader;
272 if ( StringUtils.isNotEmpty( getSourceEncoding() ) )
273 {
274 reader = ReaderFactory.newReader( file, getSourceEncoding() );
275 }
276 else
277 {
278 reader = ReaderFactory.newPlatformReader( file );
279 }
280
281 try
282 {
283 pmd.processFile( reader, sets[idx], ruleContext );
284 }
285 finally
286 {
287 reader.close();
288 }
289 }
290 catch ( UnsupportedEncodingException e1 )
291 {
292 throw new MavenReportException( "Encoding '" + getSourceEncoding() + "' is not supported.", e1 );
293 }
294 catch ( PMDException pe )
295 {
296 String msg = pe.getLocalizedMessage();
297 Throwable r = pe.getCause();
298 if ( r != null )
299 {
300 msg = msg + ": " + r.getLocalizedMessage();
301 }
302 getLog().warn( msg );
303 reportSink.ruleViolationAdded( new ProcessingErrorRuleViolation( file, msg ) );
304 }
305 catch ( FileNotFoundException e2 )
306 {
307 getLog().warn( "Error opening source file: " + file );
308 reportSink.ruleViolationAdded(
309 new ProcessingErrorRuleViolation( file, e2.getLocalizedMessage() ) );
310 }
311 catch ( Exception e3 )
312 {
313 getLog().warn( "Failure executing PMD for: " + file, e3 );
314 reportSink.ruleViolationAdded(
315 new ProcessingErrorRuleViolation( file, e3.getLocalizedMessage() ) );
316 }
317 }
318 reportSink.endFile( file );
319 }
320
321 for ( int idx = 0; idx < rulesets.length; idx++ )
322 {
323 sets[idx].end( ruleContext );
324 }
325
326 reportSink.endDocument();
327
328 return report;
329 }
330
331
332
333
334
335
336
337 private void writeNonHtml( Report report )
338 throws MavenReportException
339 {
340 Renderer r = createRenderer();
341
342 if ( r == null )
343 {
344 return;
345 }
346
347 Writer writer = null;
348
349 try
350 {
351 File targetFile = new File( targetDirectory, "pmd." + format );
352 FileOutputStream tStream = new FileOutputStream( targetFile );
353 writer = new OutputStreamWriter( tStream, getOutputEncoding() );
354
355 r.setWriter( writer );
356 r.start();
357 r.renderFileReport( report );
358 r.end();
359 writer.close();
360
361 File siteDir = getReportOutputDirectory();
362 siteDir.mkdirs();
363 FileUtils.copyFile( targetFile, new File( siteDir, "pmd." + format ) );
364 }
365 catch ( IOException ioe )
366 {
367 throw new MavenReportException( ioe.getMessage(), ioe );
368 }
369 finally
370 {
371 IOUtil.close( writer );
372 }
373 }
374
375
376
377
378
379
380
381 private String getLocationTemp( String name )
382 {
383 String loc = name;
384 if ( loc.indexOf( '/' ) != -1 )
385 {
386 loc = loc.substring( loc.lastIndexOf( '/' ) + 1 );
387 }
388 if ( loc.indexOf( '\\' ) != -1 )
389 {
390 loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 );
391 }
392 getLog().debug( "Before: " + name + " After: " + loc );
393 return loc;
394 }
395
396
397
398
399
400
401
402
403
404 public PMD getPMD()
405 throws MavenReportException
406 {
407 PMD pmd = new PMD();
408
409 if ( null != targetJdk )
410 {
411 SourceType sourceType = SourceType.getSourceTypeForId( "java " + targetJdk );
412 if ( sourceType == null )
413 {
414 throw new MavenReportException( "Unsupported targetJdk value '" + targetJdk + "'." );
415 }
416 pmd.setJavaVersion( sourceType );
417 }
418
419 return pmd;
420 }
421
422
423 public String getOutputName()
424 {
425 return "pmd";
426 }
427
428 private static ResourceBundle getBundle( Locale locale )
429 {
430 return ResourceBundle.getBundle( "pmd-report", locale, PmdReport.class.getClassLoader() );
431 }
432
433
434
435
436
437
438
439
440 public final Renderer createRenderer()
441 throws MavenReportException
442 {
443 Renderer renderer = null;
444 if ( "xml".equals( format ) )
445 {
446 renderer = new PmdXMLRenderer( getOutputEncoding() );
447 }
448 else if ( "txt".equals( format ) )
449 {
450 renderer = new TextRenderer();
451 }
452 else if ( "csv".equals( format ) )
453 {
454 renderer = new CSVRenderer();
455 }
456 else if ( "html".equals( format ) )
457 {
458 renderer = new HTMLRenderer();
459 }
460 else if ( !"".equals( format ) && !"none".equals( format ) )
461 {
462 try
463 {
464 renderer = (Renderer) Class.forName( format ).newInstance();
465 }
466 catch ( Exception e )
467 {
468 throw new MavenReportException(
469 "Can't find PMD custom format " + format + ": " + e.getClass().getName(), e );
470 }
471 }
472
473 return renderer;
474 }
475
476 private static class PmdXMLRenderer extends XMLRenderer
477 {
478 public PmdXMLRenderer( String encoding )
479 {
480 super();
481 this.encoding = encoding;
482 }
483 }
484
485
486 private static class ProcessingErrorRuleViolation
487 implements IRuleViolation
488 {
489
490 private String filename;
491
492 private String description;
493
494 public ProcessingErrorRuleViolation( File file,
495 String description )
496 {
497 filename = file.getPath();
498 this.description = description;
499 }
500
501
502 public String getFilename()
503 {
504 return this.filename;
505 }
506
507
508 public int getBeginLine()
509 {
510 return 0;
511 }
512
513
514 public int getBeginColumn()
515 {
516 return 0;
517 }
518
519
520 public int getEndLine()
521 {
522 return 0;
523 }
524
525
526 public int getEndColumn()
527 {
528 return 0;
529 }
530
531
532 public Rule getRule()
533 {
534 return null;
535 }
536
537
538 public String getDescription()
539 {
540 return this.description;
541 }
542
543
544 public String getPackageName()
545 {
546 return null;
547 }
548
549
550 public String getMethodName()
551 {
552 return null;
553 }
554
555
556 public String getClassName()
557 {
558 return null;
559 }
560
561
562 public boolean isSuppressed()
563 {
564 return false;
565 }
566
567
568 public String getVariableName()
569 {
570 return null;
571 }
572 }
573 }