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