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