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