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