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