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