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