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