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