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