1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.checkstyle;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.nio.file.Path;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33
34 import com.puppycrawl.tools.checkstyle.DefaultLogger;
35 import com.puppycrawl.tools.checkstyle.SarifLogger;
36 import com.puppycrawl.tools.checkstyle.XMLLogger;
37 import com.puppycrawl.tools.checkstyle.api.AuditListener;
38 import com.puppycrawl.tools.checkstyle.api.AutomaticBean.OutputStreamOptions;
39 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.execution.MavenSession;
42 import org.apache.maven.model.Dependency;
43 import org.apache.maven.model.Plugin;
44 import org.apache.maven.model.PluginManagement;
45 import org.apache.maven.model.Resource;
46 import org.apache.maven.plugin.descriptor.PluginDescriptor;
47 import org.apache.maven.plugins.annotations.Component;
48 import org.apache.maven.plugins.annotations.Parameter;
49 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutor;
50 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorException;
51 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorRequest;
52 import org.apache.maven.plugins.checkstyle.exec.CheckstyleResults;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.reporting.AbstractMavenReport;
55 import org.apache.maven.reporting.MavenReportException;
56 import org.codehaus.plexus.configuration.PlexusConfiguration;
57 import org.codehaus.plexus.i18n.I18N;
58 import org.codehaus.plexus.resource.ResourceManager;
59 import org.codehaus.plexus.resource.loader.FileResourceLoader;
60 import org.codehaus.plexus.util.FileUtils;
61 import org.codehaus.plexus.util.PathTool;
62
63
64
65
66
67
68 public abstract class AbstractCheckstyleReport extends AbstractMavenReport {
69 protected static final String JAVA_FILES = "**\\/*.java";
70
71 private static final String DEFAULT_CONFIG_LOCATION = "sun_checks.xml";
72
73 @Parameter(defaultValue = "${session}", readonly = true, required = true)
74 private MavenSession session;
75
76
77
78
79 @Parameter(defaultValue = "${project.build.directory}/checkstyle-cachefile")
80 protected String cacheFile;
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 @Parameter(property = "checkstyle.config.location", defaultValue = DEFAULT_CONFIG_LOCATION)
104 protected String configLocation;
105
106
107
108
109 @Parameter(property = "checkstyle.consoleOutput", defaultValue = "false")
110 protected boolean consoleOutput;
111
112
113
114
115 @Parameter(defaultValue = "false")
116 protected boolean failsOnError;
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 @Parameter(property = "checkstyle.header.file", defaultValue = "LICENSE.txt")
137 protected String headerLocation;
138
139
140
141
142
143
144 @Parameter(property = "checkstyle.skip", defaultValue = "false")
145 protected boolean skip;
146
147
148
149
150
151
152 @Parameter(property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml")
153 private File outputFile;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 @Parameter(property = "checkstyle.properties.location")
172 protected String propertiesLocation;
173
174
175
176
177 @Parameter
178 protected String propertyExpansion;
179
180
181
182
183
184
185 @Parameter(defaultValue = "${project.resources}", readonly = true)
186 protected List<Resource> resources;
187
188
189
190
191
192
193 @Parameter(defaultValue = "${project.testResources}", readonly = true)
194 protected List<Resource> testResources;
195
196
197
198
199 @Parameter(property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true)
200 protected String includes;
201
202
203
204
205
206 @Parameter(property = "checkstyle.excludes")
207 protected String excludes;
208
209
210
211
212
213 @Parameter(property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true)
214 protected String resourceIncludes;
215
216
217
218
219
220
221 @Parameter(property = "checkstyle.resourceExcludes")
222 protected String resourceExcludes;
223
224
225
226
227
228 @Parameter(property = "checkstyle.includeResources", defaultValue = "true", required = true)
229 protected boolean includeResources;
230
231
232
233
234
235 @Parameter(property = "checkstyle.includeTestResources", defaultValue = "true", required = true)
236 protected boolean includeTestResources;
237
238
239
240
241
242
243
244 @Deprecated
245 @Parameter
246 private File sourceDirectory;
247
248
249
250
251
252
253
254 @Parameter
255 private List<String> sourceDirectories;
256
257
258
259
260
261
262
263
264 @Parameter
265 @Deprecated
266 private File testSourceDirectory;
267
268
269
270
271
272
273
274 @Parameter
275 private List<String> testSourceDirectories;
276
277
278
279
280
281
282 @Parameter(defaultValue = "false")
283 protected boolean includeTestSourceDirectory;
284
285
286
287
288
289
290 @Parameter(property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file")
291 protected String suppressionsFileExpression;
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 @Parameter(property = "checkstyle.suppressions.location")
308 protected String suppressionsLocation;
309
310
311
312
313
314 @Parameter
315 private File useFile;
316
317
318
319
320
321 @Parameter(property = "checkstyle.output.format", defaultValue = "xml")
322 private String outputFileFormat;
323
324
325
326
327 @Parameter(property = "checkstyle.enable.rules.summary", defaultValue = "true")
328 private boolean enableRulesSummary;
329
330
331
332
333 @Parameter(property = "checkstyle.enable.severity.summary", defaultValue = "true")
334 private boolean enableSeveritySummary;
335
336
337
338
339 @Parameter(property = "checkstyle.enable.files.summary", defaultValue = "true")
340 private boolean enableFilesSummary;
341
342
343
344
345 @Parameter(defaultValue = "${plugin}", readonly = true, required = true)
346 private PluginDescriptor plugin;
347
348
349
350
351
352
353
354 @Parameter(property = "linkXRef", defaultValue = "true")
355 private boolean linkXRef;
356
357
358
359
360
361
362 @Parameter
363 private File xrefLocation;
364
365
366
367
368
369
370 @Parameter
371 private File xrefTestLocation;
372
373
374
375
376
377
378
379 @Parameter
380 private List<String> treeWalkerNames;
381
382
383
384
385
386
387
388 @Parameter(defaultValue = "false")
389 private boolean omitIgnoredModules;
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415 @Parameter
416 private PlexusConfiguration checkstyleRules;
417
418
419
420
421 @Parameter(
422 property = "checkstyle.output.rules.file",
423 defaultValue = "${project.build.directory}/checkstyle-rules.xml")
424 private File rulesFiles;
425
426
427
428
429
430 @Parameter(
431 defaultValue = "<?xml version=\"1.0\"?>\n"
432 + "<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n"
433 + " \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n")
434 private String checkstyleRulesHeader;
435
436
437
438
439
440
441 @Parameter(property = "checkstyle.excludeGeneratedSources", defaultValue = "false")
442 private boolean excludeGeneratedSources;
443
444
445
446 @Component
447 protected ResourceManager locator;
448
449
450
451
452 @Component(role = CheckstyleExecutor.class, hint = "default")
453 protected CheckstyleExecutor checkstyleExecutor;
454
455
456
457
458 @Component
459 private I18N i18n;
460
461 protected ByteArrayOutputStream stringOutputStream;
462
463
464 public String getName(Locale locale) {
465 return getI18nString(locale, "name");
466 }
467
468
469 public String getDescription(Locale locale) {
470 return getI18nString(locale, "description");
471 }
472
473
474
475
476
477
478 protected String getI18nString(Locale locale, String key) {
479 return i18n.getString("checkstyle-report", locale, "report.checkstyle." + key);
480 }
481
482 protected MavenProject getProject() {
483 return project;
484 }
485
486 protected List<MavenProject> getReactorProjects() {
487 return reactorProjects;
488 }
489
490
491 public void executeReport(Locale locale) throws MavenReportException {
492 checkDeprecatedParameterUsage(sourceDirectory, "sourceDirectory", "sourceDirectories");
493 checkDeprecatedParameterUsage(testSourceDirectory, "testSourceDirectory", "testSourceDirectories");
494
495 locator.addSearchPath(
496 FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath());
497 locator.addSearchPath("url", "");
498
499 locator.setOutputDirectory(new File(project.getBuild().getDirectory()));
500
501
502
503
504
505 String effectiveConfigLocation = configLocation;
506 if (checkstyleRules != null) {
507 if (!DEFAULT_CONFIG_LOCATION.equals(configLocation)) {
508 throw new MavenReportException(
509 "If you use inline configuration for rules, don't specify " + "a configLocation");
510 }
511 if (checkstyleRules.getChildCount() > 1) {
512 throw new MavenReportException("Currently only one root module is supported");
513 }
514 PlexusConfiguration checkerModule = checkstyleRules.getChild(0);
515
516 try {
517 FileUtils.forceMkdir(rulesFiles.getParentFile());
518 FileUtils.fileWrite(rulesFiles, checkstyleRulesHeader + checkerModule.toString());
519 } catch (final IOException e) {
520 throw new MavenReportException(e.getMessage(), e);
521 }
522 effectiveConfigLocation = rulesFiles.getAbsolutePath();
523 }
524 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
525
526 try {
527 CheckstyleExecutorRequest request = createRequest()
528 .setLicenseArtifacts(collectArtifacts("license"))
529 .setConfigurationArtifacts(collectArtifacts("configuration"))
530 .setOmitIgnoredModules(omitIgnoredModules)
531 .setConfigLocation(effectiveConfigLocation);
532
533 CheckstyleResults results = checkstyleExecutor.executeCheckstyle(request);
534
535 CheckstyleReportRenderer r = new CheckstyleReportRenderer(
536 getSink(),
537 i18n,
538 locale,
539 project,
540 siteTool,
541 effectiveConfigLocation,
542 linkXRef ? constructXrefLocation(xrefLocation, false) : null,
543 linkXRef ? constructXrefLocation(xrefTestLocation, true) : null,
544 linkXRef ? getTestSourceDirectories() : Collections.emptyList(),
545 enableRulesSummary,
546 enableSeveritySummary,
547 enableFilesSummary,
548 results);
549 if (treeWalkerNames != null) {
550 r.setTreeWalkerNames(treeWalkerNames);
551 }
552 r.render();
553 } catch (CheckstyleException e) {
554 throw new MavenReportException("Failed during checkstyle configuration", e);
555 } catch (CheckstyleExecutorException e) {
556 throw new MavenReportException("Failed during checkstyle execution", e);
557 } finally {
558
559 Thread.currentThread().setContextClassLoader(currentClassLoader);
560 }
561 }
562
563 private void checkDeprecatedParameterUsage(Object parameter, String name, String replacement)
564 throws MavenReportException {
565 if (parameter != null) {
566 throw new MavenReportException("You are using '" + name + "' which has been removed"
567 + " from the maven-checkstyle-plugin. " + "Please use '" + replacement
568 + "' and refer to the >>Major Version Upgrade to version 3.0.0<< " + "on the plugin site.");
569 }
570 }
571
572
573
574
575
576
577
578 protected abstract CheckstyleExecutorRequest createRequest() throws MavenReportException;
579
580 private List<Artifact> collectArtifacts(String hint) {
581 List<Artifact> artifacts = new ArrayList<>();
582
583 PluginManagement pluginManagement = project.getBuild().getPluginManagement();
584 if (pluginManagement != null) {
585 artifacts.addAll(getCheckstylePluginDependenciesAsArtifacts(pluginManagement.getPluginsAsMap(), hint));
586 }
587
588 artifacts.addAll(
589 getCheckstylePluginDependenciesAsArtifacts(project.getBuild().getPluginsAsMap(), hint));
590
591 return artifacts;
592 }
593
594 private List<Artifact> getCheckstylePluginDependenciesAsArtifacts(Map<String, Plugin> plugins, String hint) {
595 List<Artifact> artifacts = new ArrayList<>();
596
597 Plugin checkstylePlugin = plugins.get(plugin.getGroupId() + ":" + plugin.getArtifactId());
598 if (checkstylePlugin != null) {
599 for (Dependency dep : checkstylePlugin.getDependencies()) {
600
601 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
602 artifacts.add(plugin.getArtifactMap().get(depKey));
603 }
604 }
605 return artifacts;
606 }
607
608
609
610
611
612
613
614 protected AuditListener getListener() throws MavenReportException {
615 AuditListener listener = null;
616
617 if (outputFileFormat != null && !outputFileFormat.isEmpty()) {
618 File resultFile = outputFile;
619
620 OutputStream out = getOutputStream(resultFile);
621
622 if ("xml".equals(outputFileFormat)) {
623 listener = new XMLLogger(out, OutputStreamOptions.CLOSE);
624 } else if ("plain".equals(outputFileFormat)) {
625 listener = new DefaultLogger(out, OutputStreamOptions.CLOSE);
626 } else if ("sarif".equals(outputFileFormat)) {
627 try {
628 listener = new SarifLogger(out, OutputStreamOptions.CLOSE);
629 } catch (IOException e) {
630 throw new MavenReportException("Failed to create SarifLogger", e);
631 }
632 } else {
633
634 throw new MavenReportException(
635 "Invalid output file format: (" + outputFileFormat + "). Must be 'plain', 'sarif' or 'xml'.");
636 }
637 }
638
639 return listener;
640 }
641
642 private OutputStream getOutputStream(File file) throws MavenReportException {
643 File parentFile = file.getAbsoluteFile().getParentFile();
644
645 if (!parentFile.exists()) {
646 parentFile.mkdirs();
647 }
648
649 FileOutputStream fileOutputStream;
650 try {
651 fileOutputStream = new FileOutputStream(file);
652 } catch (FileNotFoundException e) {
653 throw new MavenReportException("Unable to create output stream: " + file, e);
654 }
655 return fileOutputStream;
656 }
657
658
659
660
661
662
663
664 protected DefaultLogger getConsoleListener() throws MavenReportException {
665 DefaultLogger consoleListener;
666
667 if (useFile == null) {
668 stringOutputStream = new ByteArrayOutputStream();
669 consoleListener = new DefaultLogger(stringOutputStream, OutputStreamOptions.NONE);
670 } else {
671 OutputStream out = getOutputStream(useFile);
672
673 consoleListener = new DefaultLogger(out, OutputStreamOptions.CLOSE);
674 }
675
676 return consoleListener;
677 }
678
679 private String determineRelativePath(File location) {
680 String relativePath =
681 PathTool.getRelativePath(getReportOutputDirectory().getAbsolutePath(), location.getAbsolutePath());
682 if (relativePath == null || relativePath.trim().isEmpty()) {
683 relativePath = ".";
684 }
685
686 return relativePath + "/" + location.getName();
687 }
688
689 protected List<File> getSourceDirectories() {
690 if (sourceDirectories == null) {
691 sourceDirectories = filterBuildTarget(project.getCompileSourceRoots());
692 }
693 List<File> sourceDirs = new ArrayList<>(sourceDirectories.size());
694 for (String sourceDir : sourceDirectories) {
695 sourceDirs.add(FileUtils.resolveFile(project.getBasedir(), sourceDir));
696 }
697 return sourceDirs;
698 }
699
700 protected List<File> getTestSourceDirectories() {
701 if (testSourceDirectories == null) {
702 testSourceDirectories = filterBuildTarget(project.getTestCompileSourceRoots());
703 }
704 List<File> testSourceDirs = new ArrayList<>(testSourceDirectories.size());
705 for (String testSourceDir : testSourceDirectories) {
706 testSourceDirs.add(FileUtils.resolveFile(project.getBasedir(), testSourceDir));
707 }
708 return testSourceDirs;
709 }
710
711 private List<String> filterBuildTarget(List<String> sourceDirectories) {
712 if (!excludeGeneratedSources) {
713 return sourceDirectories;
714 }
715
716 List<String> filtered = new ArrayList<>(sourceDirectories.size());
717 Path buildTarget = FileUtils.resolveFile(
718 project.getBasedir(), project.getBuild().getDirectory())
719 .toPath();
720
721 for (String sourceDir : sourceDirectories) {
722 Path src = FileUtils.resolveFile(project.getBasedir(), sourceDir).toPath();
723 if (!src.startsWith(buildTarget)) {
724 filtered.add(sourceDir);
725 }
726 }
727 return filtered;
728 }
729 }