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