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