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 net.sourceforge.pmd.PMD;
23 import net.sourceforge.pmd.PMDConfiguration;
24 import net.sourceforge.pmd.Report;
25 import net.sourceforge.pmd.RuleContext;
26 import net.sourceforge.pmd.RulePriority;
27 import net.sourceforge.pmd.RuleSetFactory;
28 import net.sourceforge.pmd.RuleSetReferenceId;
29 import net.sourceforge.pmd.RuleViolation;
30 import net.sourceforge.pmd.lang.LanguageVersion;
31 import net.sourceforge.pmd.renderers.CSVRenderer;
32 import net.sourceforge.pmd.renderers.HTMLRenderer;
33 import net.sourceforge.pmd.renderers.Renderer;
34 import net.sourceforge.pmd.renderers.TextRenderer;
35 import net.sourceforge.pmd.renderers.XMLRenderer;
36 import net.sourceforge.pmd.util.datasource.DataSource;
37 import net.sourceforge.pmd.util.datasource.FileDataSource;
38 import org.apache.maven.doxia.sink.Sink;
39 import org.apache.maven.plugins.annotations.Component;
40 import org.apache.maven.plugins.annotations.Mojo;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.plugins.annotations.ResolutionScope;
43 import org.apache.maven.reporting.MavenReportException;
44 import org.codehaus.plexus.resource.ResourceManager;
45 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
46 import org.codehaus.plexus.resource.loader.FileResourceLoader;
47 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
48 import org.codehaus.plexus.util.FileUtils;
49 import org.codehaus.plexus.util.IOUtil;
50 import org.codehaus.plexus.util.ReaderFactory;
51 import org.codehaus.plexus.util.StringUtils;
52
53 import java.io.File;
54 import java.io.FileOutputStream;
55 import java.io.IOException;
56 import java.io.OutputStreamWriter;
57 import java.io.Writer;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.Map;
63 import java.util.Properties;
64 import java.util.ResourceBundle;
65
66
67
68
69
70
71
72
73 @Mojo( name = "pmd", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
74 public class PmdReport
75 extends AbstractPmdReport
76 {
77
78
79
80
81
82
83
84 @Parameter( property = "targetJdk" )
85 private String targetJdk;
86
87
88
89
90
91
92
93
94
95
96 @Parameter( defaultValue = "java" )
97 private String language;
98
99
100
101
102
103
104
105 @Parameter( property = "minimumPriority", defaultValue = "5" )
106 private int minimumPriority = 5;
107
108
109
110
111
112
113
114 @Parameter( property = "pmd.skip", defaultValue = "false" )
115 private boolean skip;
116
117
118
119
120
121
122 @Parameter
123 private String[] rulesets = new String[]{ "java-basic", "java-unusedcode", "java-imports" };
124
125
126
127
128
129
130
131 @Parameter( property = "pmd.typeResolution", defaultValue = "false" )
132 private boolean typeResolution;
133
134
135
136 @Component
137 private ResourceManager locator;
138
139
140
141
142 public String getName( Locale locale )
143 {
144 return getBundle( locale ).getString( "report.pmd.name" );
145 }
146
147
148
149
150 public String getDescription( Locale locale )
151 {
152 return getBundle( locale ).getString( "report.pmd.description" );
153 }
154
155 public void setRulesets( String[] rules )
156 {
157 rulesets = rules;
158 }
159
160
161
162
163 public void executeReport( Locale locale )
164 throws MavenReportException
165 {
166 try
167 {
168 execute( locale );
169 }
170 finally
171 {
172 if ( getSink() != null )
173 {
174 getSink().close();
175 }
176 }
177 }
178
179 private void execute( Locale locale )
180 throws MavenReportException
181 {
182
183 locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
184 locator.addSearchPath( "url", "" );
185 locator.setOutputDirectory( targetDirectory );
186
187 if ( !skip && canGenerateReport() )
188 {
189 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
190 try
191 {
192 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
193
194 Report report = generateReport( locale );
195
196 if ( !isHtml() )
197 {
198 writeNonHtml( report );
199 }
200 }
201 finally
202 {
203 Thread.currentThread().setContextClassLoader( origLoader );
204 }
205 }
206 }
207
208 private Report generateReport( Locale locale )
209 throws MavenReportException
210 {
211 Sink sink = getSink();
212
213 PMDConfiguration pmdConfiguration = getPMDConfiguration();
214 final PmdReportListener reportSink = new PmdReportListener( getLog(), sink, getBundle( locale ), aggregate );
215 RuleContext ruleContext = new RuleContext()
216 {
217 @Override
218 public void setReport( Report report )
219 {
220 super.setReport( report );
221
222 report.addListener( reportSink );
223 }
224 };
225 reportSink.beginDocument();
226
227 RuleSetFactory ruleSetFactory = new RuleSetFactory();
228 ruleSetFactory.setMinimumPriority( RulePriority.valueOf( this.minimumPriority ) );
229 String[] sets = new String[rulesets.length];
230 try
231 {
232 for ( int idx = 0; idx < rulesets.length; idx++ )
233 {
234 String set = rulesets[idx];
235 getLog().debug( "Preparing ruleset: " + set );
236 RuleSetReferenceId id = new RuleSetReferenceId( set );
237 File ruleset = locator.getResourceAsFile( id.getRuleSetFileName(), getLocationTemp( set ) );
238 if ( null == ruleset )
239 {
240 throw new MavenReportException( "Could not resolve " + set );
241 }
242 sets[idx] = ruleset.getAbsolutePath();
243 }
244 }
245 catch ( ResourceNotFoundException e )
246 {
247 throw new MavenReportException( e.getMessage(), e );
248 }
249 catch ( FileResourceCreationException e )
250 {
251 throw new MavenReportException( e.getMessage(), e );
252 }
253 pmdConfiguration.setRuleSets( StringUtils.join( sets, "," ) );
254
255 Map<File, PmdFileInfo> files;
256 try
257 {
258 files = getFilesToProcess();
259 if ( files.isEmpty() && !"java".equals( language ) )
260 {
261 getLog().warn(
262 "No files found to process. Did you add your additional source folders like javascript? (see also build-helper-maven-plugin)" );
263 }
264 }
265 catch ( IOException e )
266 {
267 throw new MavenReportException( "Can't get file list", e );
268 }
269
270 String encoding = getSourceEncoding();
271 if ( StringUtils.isEmpty( encoding ) && !files.isEmpty() )
272 {
273 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
274 + ", i.e. build is platform dependent!" );
275 encoding = ReaderFactory.FILE_ENCODING;
276 }
277 pmdConfiguration.setSourceEncoding( encoding );
278
279 reportSink.setFiles( files );
280 List<DataSource> dataSources = new ArrayList<DataSource>( files.size() );
281 for ( File f : files.keySet() )
282 {
283 dataSources.add( new FileDataSource( f ) );
284 }
285
286 try
287 {
288 List<Renderer> renderers = Collections.emptyList();
289
290
291
292
293
294 pmdConfiguration.setThreads( 0 );
295
296 PMD.processFiles( pmdConfiguration, ruleSetFactory, dataSources, ruleContext, renderers );
297 }
298 catch ( Exception e )
299 {
300 getLog().warn( "Failure executing PMD: " + e.getLocalizedMessage(), e );
301 }
302
303 try
304 {
305 reportSink.endDocument();
306 }
307 catch ( IOException e )
308 {
309 getLog().warn( "Failure creating the report: " + e.getLocalizedMessage(), e );
310 }
311
312
313 Report report = new Report();
314 for ( RuleViolation v : reportSink.getViolations() )
315 {
316 report.addRuleViolation( v );
317 }
318 return report;
319 }
320
321
322
323
324
325
326
327 protected String getLocationTemp( String name )
328 {
329 String loc = name;
330 if ( loc.indexOf( '/' ) != -1 )
331 {
332 loc = loc.substring( loc.lastIndexOf( '/' ) + 1 );
333 }
334 if ( loc.indexOf( '\\' ) != -1 )
335 {
336 loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 );
337 }
338
339
340
341
342
343 loc = loc.replaceAll( "[\\?\\:\\&\\=\\%]", "_" );
344
345 if ( !loc.endsWith( ".xml" ) )
346 {
347 loc = loc + ".xml";
348 }
349
350 getLog().debug( "Before: " + name + " After: " + loc );
351 return loc;
352 }
353
354
355
356
357
358
359
360 private void writeNonHtml( Report report )
361 throws MavenReportException
362 {
363 Renderer r = createRenderer();
364
365 if ( r == null )
366 {
367 return;
368 }
369
370 Writer writer = null;
371 FileOutputStream tStream = null;
372 try
373 {
374 targetDirectory.mkdirs();
375 File targetFile = new File( targetDirectory, "pmd." + format );
376 tStream = new FileOutputStream( targetFile );
377 writer = new OutputStreamWriter( tStream, getOutputEncoding() );
378
379 r.setWriter( writer );
380 r.start();
381 r.renderFileReport( report );
382 r.end();
383 writer.close();
384
385 if ( includeXmlInSite ) {
386 File siteDir = getReportOutputDirectory();
387 siteDir.mkdirs();
388 FileUtils.copyFile( targetFile, new File( siteDir, "pmd." + format ) );
389 }
390 }
391 catch ( IOException ioe )
392 {
393 throw new MavenReportException( ioe.getMessage(), ioe );
394 }
395 finally
396 {
397 IOUtil.close( writer );
398 IOUtil.close( tStream );
399 }
400 }
401
402
403
404
405
406
407
408
409
410 public PMDConfiguration getPMDConfiguration()
411 throws MavenReportException
412 {
413 PMDConfiguration configuration = new PMDConfiguration();
414 LanguageVersion languageVersion = null;
415
416 if ( null != targetJdk )
417 {
418 languageVersion = LanguageVersion.findByTerseName( "java " + targetJdk );
419 if ( languageVersion == null )
420 {
421 throw new MavenReportException( "Unsupported targetJdk value '" + targetJdk + "'." );
422 }
423 }
424 else if ( "javascript".equals( language ) || "ecmascript".equals( language ) )
425 {
426 languageVersion = LanguageVersion.ECMASCRIPT;
427 }
428 if ( languageVersion != null )
429 {
430 getLog().debug( "Using language " + languageVersion );
431 configuration.setDefaultLanguageVersion( languageVersion );
432 }
433
434 if ( typeResolution )
435 {
436 try
437 {
438 @SuppressWarnings( "unchecked" )
439 List<String> classpath =
440 includeTests ? project.getTestClasspathElements() : project.getCompileClasspathElements();
441 getLog().debug( "Using aux classpath: " + classpath );
442 configuration.prependClasspath( StringUtils.join( classpath.iterator(), File.pathSeparator ) );
443 }
444 catch ( Exception e )
445 {
446 throw new MavenReportException( e.getMessage(), e );
447 }
448 }
449
450 return configuration;
451 }
452
453
454
455
456 public String getOutputName()
457 {
458 return "pmd";
459 }
460
461 private static ResourceBundle getBundle( Locale locale )
462 {
463 return ResourceBundle.getBundle( "pmd-report", locale, PmdReport.class.getClassLoader() );
464 }
465
466
467
468
469
470
471
472
473 public final Renderer createRenderer()
474 throws MavenReportException
475 {
476 Renderer renderer = null;
477 if ( "xml".equals( format ) )
478 {
479 renderer = new XMLRenderer( getOutputEncoding() );
480 }
481 else if ( "txt".equals( format ) )
482 {
483 renderer = new TextRenderer( );
484 }
485 else if ( "csv".equals( format ) )
486 {
487 renderer = new CSVRenderer( );
488 }
489 else if ( "html".equals( format ) )
490 {
491 renderer = new HTMLRenderer( );
492 }
493 else if ( !"".equals( format ) && !"none".equals( format ) )
494 {
495 try
496 {
497 renderer = (Renderer) Class.forName( format ).getConstructor( Properties.class ).newInstance(
498 new Properties() );
499 }
500 catch ( Exception e )
501 {
502 throw new MavenReportException(
503 "Can't find PMD custom format " + format + ": " + e.getClass().getName(), e );
504 }
505 }
506
507 return renderer;
508 }
509
510 }