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