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.plugin.MojoExecution;
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
55
56
57
58
59
60 public abstract class AbstractPmdReport extends AbstractMavenReport {
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
77 @Parameter(property = "format", defaultValue = "xml")
78 protected String format = "xml";
79
80
81
82
83
84 @Parameter(property = "linkXRef", defaultValue = "true")
85 private boolean linkXRef;
86
87
88
89
90
91
92 @Parameter
93 private File xrefLocation;
94
95
96
97
98
99
100 @Parameter
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
163 @Parameter(property = "aggregate", defaultValue = "false")
164 @Deprecated
165 protected boolean aggregate;
166
167
168
169
170
171
172 @Parameter(defaultValue = "false")
173 protected boolean includeXmlInReports;
174
175
176
177
178
179
180
181
182
183 @Parameter(defaultValue = "false")
184 protected boolean skipEmptyReport;
185
186
187
188
189
190
191
192
193
194 @Parameter(property = "pmd.excludeFromFailureFile", defaultValue = "")
195 protected String excludeFromFailureFile;
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210 @Parameter(defaultValue = "true", property = "pmd.showPmdLog")
211 @Deprecated
212 protected boolean showPmdLog = true;
213
214
215
216
217 private boolean warnedAboutShowPmdLog = false;
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
243
244
245
246
247
248
249
250
251
252
253 @Parameter
254 private Map<String, String> jdkToolchain;
255
256
257
258
259
260
261
262
263
264 @Parameter(defaultValue = "${session}", required = true, readonly = true)
265 protected MavenSession session;
266
267 @Component
268 private ToolchainManager toolchainManager;
269
270
271 protected Map<File, PmdFileInfo> filesToProcess;
272
273 @Override
274 protected MavenProject getProject() {
275 return project;
276 }
277
278 protected List<MavenProject> getReactorProjects() {
279 return reactorProjects;
280 }
281
282 protected MojoExecution getMojoExecution() {
283 return mojoExecution;
284 }
285
286 protected String constructXrefLocation(boolean test) {
287 String location = null;
288 if (linkXRef) {
289 File xrefLocation = getXrefLocation(test);
290
291 String relativePath = PathTool.getRelativePath(
292 getReportOutputDirectory().getAbsolutePath(), xrefLocation.getAbsolutePath());
293 if (relativePath == null || relativePath.isEmpty()) {
294 relativePath = ".";
295 }
296 relativePath = relativePath + "/" + xrefLocation.getName();
297 if (xrefLocation.exists()) {
298
299 location = relativePath;
300 } else {
301
302 Reporting reporting = project.getModel().getReporting();
303 List<ReportPlugin> reportPlugins =
304 reporting != null ? reporting.getPlugins() : Collections.<ReportPlugin>emptyList();
305 for (ReportPlugin plugin : reportPlugins) {
306 String artifactId = plugin.getArtifactId();
307 if ("maven-jxr-plugin".equals(artifactId)) {
308 location = relativePath;
309 }
310 }
311 }
312
313 if (location == null) {
314 getLog().warn("Unable to locate" + (test ? " Test" : "") + " Source XRef to link to - DISABLED");
315 }
316 }
317 return location;
318 }
319
320 protected File getXrefLocation(boolean test) {
321 File location = test ? xrefTestLocation : xrefLocation;
322 return location != null ? location : new File(getReportOutputDirectory(), test ? "xref-test" : "xref");
323 }
324
325
326
327
328
329
330
331
332 protected Map<File, PmdFileInfo> getFilesToProcess() throws IOException {
333 if (aggregate && !project.isExecutionRoot()) {
334 return Collections.emptyMap();
335 }
336
337 if (excludeRoots == null) {
338 excludeRoots = new File[0];
339 }
340
341 Collection<File> excludeRootFiles = new HashSet<>(excludeRoots.length);
342
343 for (File file : excludeRoots) {
344 if (file.isDirectory()) {
345 excludeRootFiles.add(file);
346 }
347 }
348
349 List<PmdFileInfo> directories = new ArrayList<>();
350
351 if (null == compileSourceRoots) {
352 compileSourceRoots = project.getCompileSourceRoots();
353 }
354 if (compileSourceRoots != null) {
355 for (String root : compileSourceRoots) {
356 File sroot = new File(root);
357 if (sroot.exists()) {
358 String sourceXref = constructXrefLocation(false);
359 directories.add(new PmdFileInfo(project, sroot, sourceXref));
360 }
361 }
362 }
363
364 if (null == testSourceRoots) {
365 testSourceRoots = project.getTestCompileSourceRoots();
366 }
367 if (includeTests && testSourceRoots != null) {
368 for (String root : testSourceRoots) {
369 File sroot = new File(root);
370 if (sroot.exists()) {
371 String testXref = constructXrefLocation(true);
372 directories.add(new PmdFileInfo(project, sroot, testXref));
373 }
374 }
375 }
376 if (isAggregator()) {
377 for (MavenProject localProject : getAggregatedProjects()) {
378 List<String> localCompileSourceRoots = localProject.getCompileSourceRoots();
379 for (String root : localCompileSourceRoots) {
380 File sroot = new File(root);
381 if (sroot.exists()) {
382 String sourceXref = constructXrefLocation(false);
383 directories.add(new PmdFileInfo(localProject, sroot, sourceXref));
384 }
385 }
386 if (includeTests) {
387 List<String> localTestCompileSourceRoots = localProject.getTestCompileSourceRoots();
388 for (String root : localTestCompileSourceRoots) {
389 File sroot = new File(root);
390 if (sroot.exists()) {
391 String testXref = constructXrefLocation(true);
392 directories.add(new PmdFileInfo(localProject, sroot, testXref));
393 }
394 }
395 }
396 }
397 }
398
399 String excluding = getExcludes();
400 getLog().debug("Exclusions: " + excluding);
401 String including = getIncludes();
402 getLog().debug("Inclusions: " + including);
403
404 Map<File, PmdFileInfo> files = new TreeMap<>();
405
406 for (PmdFileInfo finfo : directories) {
407 getLog().debug("Searching for files in directory "
408 + finfo.getSourceDirectory().toString());
409 File sourceDirectory = finfo.getSourceDirectory();
410 if (sourceDirectory.isDirectory() && !isDirectoryExcluded(excludeRootFiles, sourceDirectory)) {
411 List<File> newfiles = FileUtils.getFiles(sourceDirectory, including, excluding);
412 for (File newfile : newfiles) {
413 files.put(newfile.getCanonicalFile(), finfo);
414 }
415 }
416 }
417
418 return files;
419 }
420
421 private boolean isDirectoryExcluded(Collection<File> excludeRootFiles, File sourceDirectoryToCheck) {
422 boolean returnVal = false;
423 for (File excludeDir : excludeRootFiles) {
424 try {
425 if (sourceDirectoryToCheck
426 .getCanonicalFile()
427 .toPath()
428 .startsWith(excludeDir.getCanonicalFile().toPath())) {
429 getLog().debug("Directory " + sourceDirectoryToCheck.getAbsolutePath()
430 + " has been excluded as it matches excludeRoot "
431 + excludeDir.getAbsolutePath());
432 returnVal = true;
433 break;
434 }
435 } catch (IOException e) {
436 getLog().warn("Error while checking " + sourceDirectoryToCheck + " whether it should be excluded.", e);
437 }
438 }
439 return returnVal;
440 }
441
442
443
444
445
446
447 private String getIncludes() {
448 Collection<String> patterns = new LinkedHashSet<>();
449 if (includes != null) {
450 patterns.addAll(includes);
451 }
452 if (patterns.isEmpty()) {
453 patterns.add("**/*.java");
454 }
455 return StringUtils.join(patterns.iterator(), ",");
456 }
457
458
459
460
461
462
463 private String getExcludes() {
464 Collection<String> patterns = new LinkedHashSet<>(FileUtils.getDefaultExcludesAsList());
465 if (excludes != null) {
466 patterns.addAll(excludes);
467 }
468 return StringUtils.join(patterns.iterator(), ",");
469 }
470
471 protected boolean isXml() {
472 return "xml".equals(format);
473 }
474
475 protected boolean canGenerateReportInternal() throws MavenReportException {
476 if (!showPmdLog && !warnedAboutShowPmdLog) {
477 getLog().warn("The parameter \"showPmdLog\" has been deprecated and will be removed."
478 + "Setting it to \"false\" has no effect.");
479 warnedAboutShowPmdLog = true;
480 }
481
482 if (aggregate && !project.isExecutionRoot()) {
483 return false;
484 }
485
486 if (!isAggregator() && "pom".equalsIgnoreCase(project.getPackaging())) {
487 return false;
488 }
489
490
491
492 if (isXml()) {
493 return true;
494 }
495 try {
496 filesToProcess = getFilesToProcess();
497 if (filesToProcess.isEmpty()) {
498 return false;
499 }
500 } catch (IOException e) {
501 throw new MavenReportException("Failed to determine files to process for PMD", e);
502 }
503 return true;
504 }
505
506 protected String determineCurrentRootLogLevel() {
507 String logLevel = System.getProperty("org.slf4j.simpleLogger.defaultLogLevel");
508 if (logLevel == null) {
509 logLevel = System.getProperty("maven.logging.root.level");
510 }
511 if (logLevel == null) {
512
513 logLevel = "info";
514 }
515 return logLevel;
516 }
517
518 static String getPmdVersion() {
519 return PMDVersion.VERSION;
520 }
521
522
523
524 protected final Toolchain getToolchain() {
525 Toolchain tc = null;
526
527 if (jdkToolchain != null) {
528
529 try {
530 Method getToolchainsMethod = toolchainManager
531 .getClass()
532 .getMethod("getToolchains", MavenSession.class, String.class, Map.class);
533
534 @SuppressWarnings("unchecked")
535 List<Toolchain> tcs =
536 (List<Toolchain>) getToolchainsMethod.invoke(toolchainManager, session, "jdk", jdkToolchain);
537
538 if (tcs != null && !tcs.isEmpty()) {
539 tc = tcs.get(0);
540 }
541 } catch (NoSuchMethodException
542 | SecurityException
543 | IllegalAccessException
544 | IllegalArgumentException
545 | InvocationTargetException e) {
546
547 }
548 }
549
550 if (tc == null) {
551 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
552 }
553
554 return tc;
555 }
556
557 protected boolean isAggregator() {
558
559 return aggregate;
560 }
561
562
563 protected Collection<MavenProject> getAggregatedProjects() {
564 Map<Path, MavenProject> reactorProjectsMap = new HashMap<>();
565 for (MavenProject reactorProject : this.reactorProjects) {
566 reactorProjectsMap.put(reactorProject.getBasedir().toPath(), reactorProject);
567 }
568
569 return modulesForAggregatedProject(project, reactorProjectsMap);
570 }
571
572
573
574
575
576
577
578
579 private Set<MavenProject> modulesForAggregatedProject(
580 MavenProject aggregatedProject, Map<Path, MavenProject> reactorProjectsMap) {
581
582
583
584
585
586 if (aggregatedProject.getModules().isEmpty()) {
587 return Collections.singleton(aggregatedProject);
588 }
589
590 List<Path> modulePaths = new LinkedList<Path>();
591 for (String module : aggregatedProject.getModules()) {
592 modulePaths.add(new File(aggregatedProject.getBasedir(), module).toPath());
593 }
594
595 Set<MavenProject> aggregatedModules = new LinkedHashSet<>();
596
597 for (Path modulePath : modulePaths) {
598 MavenProject module = reactorProjectsMap.remove(modulePath);
599 if (module != null) {
600 aggregatedModules.addAll(modulesForAggregatedProject(module, reactorProjectsMap));
601 }
602 }
603
604 return aggregatedModules;
605 }
606 }