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 }