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