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