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