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.HashSet;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.TreeMap;
32
33 import net.sourceforge.pmd.PMD;
34
35 import org.apache.maven.doxia.siterenderer.Renderer;
36 import org.apache.maven.model.ReportPlugin;
37 import org.apache.maven.plugins.annotations.Component;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.reporting.AbstractMavenReport;
41 import org.codehaus.plexus.util.FileUtils;
42 import org.codehaus.plexus.util.PathTool;
43 import org.codehaus.plexus.util.ReaderFactory;
44 import org.codehaus.plexus.util.StringUtils;
45
46
47
48
49
50
51
52 public abstract class AbstractPmdReport
53 extends AbstractMavenReport
54 {
55
56
57
58 @Parameter( property = "project.build.directory", required = true )
59 protected File targetDirectory;
60
61
62
63
64
65
66 @Parameter( property = "project.reporting.outputDirectory", required = true )
67 protected File outputDirectory;
68
69
70
71
72 @Component
73 private Renderer siteRenderer;
74
75
76
77
78 @Parameter( defaultValue = "${project}", readonly = true, required = true )
79 protected MavenProject project;
80
81
82
83
84
85
86 @Parameter( property = "format", defaultValue = "xml" )
87 protected String format = "xml";
88
89
90
91
92
93 @Parameter( property = "linkXRef", defaultValue = "true" )
94 private boolean linkXRef;
95
96
97
98
99 @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref" )
100 private File xrefLocation;
101
102
103
104
105 @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref-test" )
106 private File xrefTestLocation;
107
108
109
110
111
112
113
114
115
116 @Parameter
117 private List<String> excludes;
118
119
120
121
122
123
124
125 @Parameter
126 private List<String> includes;
127
128
129
130
131
132
133 @Parameter( defaultValue = "${project.compileSourceRoots}" )
134 private List<String> compileSourceRoots;
135
136
137
138
139
140
141 @Parameter( defaultValue = "${project.testCompileSourceRoots}" )
142 private List<String> testSourceRoots;
143
144
145
146
147
148
149 @Parameter
150 private File[] excludeRoots;
151
152
153
154
155
156
157 @Parameter( defaultValue = "false" )
158 protected boolean includeTests;
159
160
161
162
163
164
165 @Parameter( property = "aggregate", defaultValue = "false" )
166 protected boolean aggregate;
167
168
169
170
171
172
173 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
174 private String sourceEncoding;
175
176
177
178
179
180
181 @Parameter( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}" )
182 private String outputEncoding;
183
184
185
186
187 @Parameter( property = "reactorProjects", readonly = true )
188 protected List<MavenProject> reactorProjects;
189
190
191
192
193
194
195 @Parameter( defaultValue = "false" )
196 protected boolean includeXmlInSite;
197
198
199
200
201
202
203
204 @Parameter( defaultValue = "true" )
205 protected boolean skipEmptyReport;
206
207
208
209
210
211
212
213
214
215 @Parameter( property = "pmd.excludeFromFailureFile", defaultValue = "" )
216 protected String excludeFromFailureFile;
217
218
219
220 protected Map<File, PmdFileInfo> filesToProcess;
221
222
223
224
225 @Override
226 protected MavenProject getProject()
227 {
228 return project;
229 }
230
231
232
233
234 @Override
235 protected Renderer getSiteRenderer()
236 {
237 return siteRenderer;
238 }
239
240 protected String constructXRefLocation( boolean test )
241 {
242 String location = null;
243 if ( linkXRef )
244 {
245 File xrefLoc = test ? xrefTestLocation : xrefLocation;
246
247 String relativePath =
248 PathTool.getRelativePath( outputDirectory.getAbsolutePath(), xrefLoc.getAbsolutePath() );
249 if ( StringUtils.isEmpty( relativePath ) )
250 {
251 relativePath = ".";
252 }
253 relativePath = relativePath + "/" + xrefLoc.getName();
254 if ( xrefLoc.exists() )
255 {
256
257 location = relativePath;
258 }
259 else
260 {
261
262 @SuppressWarnings( "unchecked" )
263 List<ReportPlugin> reportPlugins = project.getReportPlugins();
264 for ( ReportPlugin plugin : reportPlugins )
265 {
266 String artifactId = plugin.getArtifactId();
267 if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
268 {
269 location = relativePath;
270 }
271 }
272 }
273
274 if ( location == null )
275 {
276 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
277 }
278 }
279 return location;
280 }
281
282
283
284
285
286
287
288 protected Map<File, PmdFileInfo> getFilesToProcess()
289 throws IOException
290 {
291 if ( aggregate && !project.isExecutionRoot() )
292 {
293 return Collections.emptyMap();
294 }
295
296 if ( excludeRoots == null )
297 {
298 excludeRoots = new File[0];
299 }
300
301 Collection<File> excludeRootFiles = new HashSet<>( excludeRoots.length );
302
303 for ( File file : excludeRoots )
304 {
305 if ( file.isDirectory() )
306 {
307 excludeRootFiles.add( file );
308 }
309 }
310
311 List<PmdFileInfo> directories = new ArrayList<>();
312
313 if ( null == compileSourceRoots )
314 {
315 compileSourceRoots = project.getCompileSourceRoots();
316 }
317 if ( compileSourceRoots != null )
318 {
319 for ( String root : compileSourceRoots )
320 {
321 File sroot = new File( root );
322 if ( sroot.exists() )
323 {
324 String sourceXref = constructXRefLocation( false );
325 directories.add( new PmdFileInfo( project, sroot, sourceXref ) );
326 }
327 }
328 }
329
330 if ( null == testSourceRoots )
331 {
332 testSourceRoots = project.getTestCompileSourceRoots();
333 }
334 if ( includeTests )
335 {
336 if ( testSourceRoots != null )
337 {
338 for ( String root : testSourceRoots )
339 {
340 File sroot = new File( root );
341 if ( sroot.exists() )
342 {
343 String testXref = constructXRefLocation( true );
344 directories.add( new PmdFileInfo( project, sroot, testXref ) );
345 }
346 }
347 }
348 }
349 if ( aggregate )
350 {
351 for ( MavenProject localProject : reactorProjects )
352 {
353 @SuppressWarnings( "unchecked" )
354 List<String> localCompileSourceRoots = localProject.getCompileSourceRoots();
355 for ( String root : localCompileSourceRoots )
356 {
357 File sroot = new File( root );
358 if ( sroot.exists() )
359 {
360 String sourceXref = constructXRefLocation( false );
361 directories.add( new PmdFileInfo( localProject, sroot, sourceXref ) );
362 }
363 }
364 if ( includeTests )
365 {
366 @SuppressWarnings( "unchecked" )
367 List<String> localTestCompileSourceRoots = localProject.getTestCompileSourceRoots();
368 for ( String root : localTestCompileSourceRoots )
369 {
370 File sroot = new File( root );
371 if ( sroot.exists() )
372 {
373 String testXref = constructXRefLocation( true );
374 directories.add( new PmdFileInfo( localProject, sroot, testXref ) );
375 }
376 }
377 }
378 }
379
380 }
381
382 String excluding = getExcludes();
383 getLog().debug( "Exclusions: " + excluding );
384 String including = getIncludes();
385 getLog().debug( "Inclusions: " + including );
386
387 Map<File, PmdFileInfo> files = new TreeMap<>();
388
389 for ( PmdFileInfo finfo : directories )
390 {
391 getLog().debug( "Searching for files in directory " + finfo.getSourceDirectory().toString() );
392 File sourceDirectory = finfo.getSourceDirectory();
393 if ( sourceDirectory.isDirectory() && !isDirectoryExcluded( excludeRootFiles, sourceDirectory ) )
394 {
395 List<File> newfiles = FileUtils.getFiles( sourceDirectory, including, excluding );
396 for ( File newfile : newfiles )
397 {
398 files.put( newfile.getCanonicalFile(), finfo );
399 }
400 }
401 }
402
403 return files;
404 }
405
406 private boolean isDirectoryExcluded( Collection<File> excludeRootFiles, File sourceDirectoryToCheck )
407 {
408 boolean returnVal = false;
409 for ( File excludeDir : excludeRootFiles )
410 {
411 try
412 {
413 if ( sourceDirectoryToCheck.getCanonicalPath().startsWith( excludeDir.getCanonicalPath() ) )
414 {
415 getLog().debug( "Directory " + sourceDirectoryToCheck.getAbsolutePath()
416 + " has been excluded as it matches excludeRoot "
417 + excludeDir.getAbsolutePath() );
418 returnVal = true;
419 break;
420 }
421 }
422 catch ( IOException e )
423 {
424 getLog().warn( "Error while checking " + sourceDirectoryToCheck
425 + " whether it should be excluded.", e );
426 }
427 }
428 return returnVal;
429 }
430
431
432
433
434
435
436 private String getIncludes()
437 {
438 Collection<String> patterns = new LinkedHashSet<>();
439 if ( includes != null )
440 {
441 patterns.addAll( includes );
442 }
443 if ( patterns.isEmpty() )
444 {
445 patterns.add( "**/*.java" );
446 }
447 return StringUtils.join( patterns.iterator(), "," );
448 }
449
450
451
452
453
454
455 private String getExcludes()
456 {
457 Collection<String> patterns = new LinkedHashSet<>( FileUtils.getDefaultExcludesAsList() );
458 if ( excludes != null )
459 {
460 patterns.addAll( excludes );
461 }
462 return StringUtils.join( patterns.iterator(), "," );
463 }
464
465 protected boolean isHtml()
466 {
467 return "html".equals( format );
468 }
469
470 protected boolean isXml()
471 {
472 return "xml".equals( format );
473 }
474
475
476
477
478 @Override
479 public boolean canGenerateReport()
480 {
481 if ( aggregate && !project.isExecutionRoot() )
482 {
483 return false;
484 }
485
486 if ( "pom".equals( project.getPackaging() ) && !aggregate )
487 {
488 return false;
489 }
490
491
492
493 if ( isXml() )
494 {
495 return true;
496 }
497 try
498 {
499 filesToProcess = getFilesToProcess();
500 if ( filesToProcess.isEmpty() )
501 {
502 return false;
503 }
504 }
505 catch ( IOException e )
506 {
507 getLog().error( e );
508 }
509 return true;
510 }
511
512
513
514
515 @Override
516 protected String getOutputDirectory()
517 {
518 return outputDirectory.getAbsolutePath();
519 }
520
521 protected String getSourceEncoding()
522 {
523 return sourceEncoding;
524 }
525
526
527
528
529
530
531
532 protected String getOutputEncoding()
533 {
534 return ( outputEncoding != null ) ? outputEncoding : ReaderFactory.UTF_8;
535 }
536
537 static String getPmdVersion()
538 {
539 try
540 {
541 return (String) PMD.class.getField( "VERSION" ).get( null );
542 }
543 catch ( IllegalAccessException e )
544 {
545 throw new RuntimeException( "PMD VERSION field not accessible", e );
546 }
547 catch ( NoSuchFieldException e )
548 {
549 throw new RuntimeException( "PMD VERSION field not found", e );
550 }
551 }
552 }