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