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