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.net.URL;
29 import java.util.Calendar;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.ResourceBundle;
36
37 import org.apache.maven.doxia.siterenderer.Renderer;
38 import org.apache.maven.doxia.tools.SiteTool;
39 import org.apache.maven.model.ReportPlugin;
40 import org.apache.maven.plugin.checkstyle.rss.CheckstyleRssGenerator;
41 import org.apache.maven.plugin.checkstyle.rss.CheckstyleRssGeneratorRequest;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.reporting.AbstractMavenReport;
44 import org.apache.maven.reporting.MavenReportException;
45 import org.codehaus.plexus.resource.ResourceManager;
46 import org.codehaus.plexus.resource.loader.FileResourceLoader;
47 import org.codehaus.plexus.util.PathTool;
48 import org.codehaus.plexus.util.StringUtils;
49
50 import com.puppycrawl.tools.checkstyle.DefaultLogger;
51 import com.puppycrawl.tools.checkstyle.XMLLogger;
52 import com.puppycrawl.tools.checkstyle.api.AuditListener;
53 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
54
55
56
57
58
59
60
61
62
63
64
65 public class CheckstyleReport
66 extends AbstractMavenReport
67 {
68 public static final String PLUGIN_RESOURCES = "org/apache/maven/plugin/checkstyle";
69
70
71
72
73 private static final Map FORMAT_TO_CONFIG_LOCATION;
74
75 static
76 {
77 Map fmt2Cfg = new HashMap();
78
79 fmt2Cfg.put( "sun", "config/sun_checks.xml" );
80 fmt2Cfg.put( "turbine", "config/turbine_checks.xml" );
81 fmt2Cfg.put( "avalon", "config/avalon_checks.xml" );
82 fmt2Cfg.put( "maven", "config/maven_checks.xml" );
83
84 FORMAT_TO_CONFIG_LOCATION = Collections.unmodifiableMap( fmt2Cfg );
85 }
86
87
88
89
90
91
92
93 private boolean skip;
94
95
96
97
98
99
100
101
102
103
104 private File outputDirectory;
105
106
107
108
109
110
111
112 private boolean enableRulesSummary;
113
114
115
116
117
118
119
120 private boolean enableSeveritySummary;
121
122
123
124
125
126
127
128 private boolean enableFilesSummary;
129
130
131
132
133
134
135 private boolean enableRSS;
136
137
138
139
140
141
142
143 private String includes;
144
145
146
147
148
149
150
151 private String excludes;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 private String configLocation;
188
189
190
191
192
193
194
195
196 private String format;
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 private String propertiesLocation;
221
222
223
224
225
226
227
228
229 private File propertiesFile;
230
231
232
233
234
235
236
237
238 private URL propertiesURL;
239
240
241
242
243
244
245 private String propertyExpansion;
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271 private String headerLocation;
272
273
274
275
276
277
278
279
280
281 private File headerFile;
282
283
284
285
286
287
288 private String cacheFile;
289
290
291
292
293
294
295
296 private File useFile;
297
298
299
300
301
302
303
304
305
306 protected SiteTool siteTool;
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328 private String suppressionsLocation;
329
330
331
332
333
334
335
336
337 private String suppressionsFileExpression;
338
339
340
341
342
343
344
345
346
347
348
349 private String suppressionsFile;
350
351
352
353
354
355
356
357
358
359 private File outputFile;
360
361
362
363
364
365
366
367 private String outputFileFormat;
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 private String packageNamesLocation;
387
388
389
390
391
392
393
394
395 private String packageNamesFile;
396
397
398
399
400
401
402 private boolean failsOnError;
403
404
405
406
407
408
409
410 private File sourceDirectory;
411
412
413
414
415
416
417
418
419 private File testSourceDirectory;
420
421
422
423
424
425
426
427 private boolean includeTestSourceDirectory;
428
429
430
431
432
433
434
435
436 private MavenProject project;
437
438
439
440
441
442
443 private boolean consoleOutput;
444
445
446
447
448
449
450
451
452 private boolean linkXRef;
453
454
455
456
457
458
459 private File xrefLocation;
460
461
462
463
464
465
466
467
468
469 private String encoding;
470
471
472
473
474
475
476 private Renderer siteRenderer;
477
478 private ByteArrayOutputStream stringOutputStream;
479
480
481
482
483
484
485 private ResourceManager locator;
486
487
488
489
490
491
492
493
494
495 protected CheckstyleRssGenerator checkstyleRssGenerator;
496
497
498
499
500
501
502
503 protected CheckstyleExecutor checkstyleExecutor;
504
505
506 public String getName( Locale locale )
507 {
508 return getBundle( locale ).getString( "report.checkstyle.name" );
509 }
510
511
512 public String getDescription( Locale locale )
513 {
514 return getBundle( locale ).getString( "report.checkstyle.description" );
515 }
516
517
518 protected String getOutputDirectory()
519 {
520 return outputDirectory.getAbsolutePath();
521 }
522
523
524 protected MavenProject getProject()
525 {
526 return project;
527 }
528
529
530 protected Renderer getSiteRenderer()
531 {
532 return siteRenderer;
533 }
534
535
536 public void executeReport( Locale locale )
537 throws MavenReportException
538 {
539 if ( !skip )
540 {
541 mergeDeprecatedInfo();
542
543 locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
544 locator.addSearchPath( "url", "" );
545
546 locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
547
548 if ( !canGenerateReport() )
549 {
550 getLog().info( "Source directory does not exist - skipping report." );
551 return;
552 }
553
554
555
556
557
558
559
560
561 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
562
563 try
564 {
565 CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
566 request.setConsoleListener( getConsoleListener() ).setConsoleOutput( consoleOutput )
567 .setExcludes( excludes ).setFailsOnError( failsOnError ).setIncludes( includes )
568 .setIncludeTestSourceDirectory( includeTestSourceDirectory ).setListener( getListener() )
569 .setLog( getLog() ).setProject( project ).setSourceDirectory( sourceDirectory )
570 .setStringOutputStream( stringOutputStream ).setSuppressionsLocation( suppressionsLocation )
571 .setTestSourceDirectory( testSourceDirectory ).setConfigLocation( configLocation )
572 .setPropertyExpansion( propertyExpansion ).setHeaderLocation( headerLocation )
573 .setCacheFile( cacheFile ).setSuppressionsFileExpression( suppressionsFileExpression )
574 .setEncoding( encoding ).setPropertiesLocation( propertiesLocation );
575
576 CheckstyleResults results = checkstyleExecutor.executeCheckstyle( request );
577
578 ResourceBundle bundle = getBundle( locale );
579 generateReportStatics();
580 generateMainReport( results, bundle );
581 if ( enableRSS )
582 {
583 CheckstyleRssGeneratorRequest checkstyleRssGeneratorRequest =
584 new CheckstyleRssGeneratorRequest( this.project, this.getCopyright(), outputDirectory, getLog() );
585 checkstyleRssGenerator.generateRSS( results, checkstyleRssGeneratorRequest );
586 }
587
588 }
589 catch ( CheckstyleException e )
590 {
591 throw new MavenReportException( "Failed during checkstyle configuration", e );
592 }
593 catch (CheckstyleExecutorException e)
594 {
595 throw new MavenReportException( "Failed during checkstyle execution", e );
596 }
597 finally
598 {
599
600 Thread.currentThread().setContextClassLoader( currentClassLoader );
601 }
602 }
603 }
604
605 private void generateReportStatics()
606 throws MavenReportException
607 {
608 ReportResource rresource = new ReportResource( PLUGIN_RESOURCES, outputDirectory );
609 try
610 {
611 rresource.copy( "images/rss.png" );
612 }
613 catch ( IOException e )
614 {
615 throw new MavenReportException( "Unable to copy static resources.", e );
616 }
617 }
618
619
620 private String getCopyright()
621 {
622 String copyright;
623 int currentYear = Calendar.getInstance().get( Calendar.YEAR );
624 if ( StringUtils.isNotEmpty( project.getInceptionYear() )
625 && !String.valueOf( currentYear ).equals( project.getInceptionYear() ) )
626 {
627 copyright = project.getInceptionYear() + " - " + currentYear;
628 }
629 else
630 {
631 copyright = String.valueOf( currentYear );
632 }
633
634 if ( ( project.getOrganization() != null ) && StringUtils.isNotEmpty( project.getOrganization().getName() ) )
635 {
636 copyright = copyright + " " + project.getOrganization().getName();
637 }
638 return copyright;
639 }
640
641 private void generateMainReport( CheckstyleResults results, ResourceBundle bundle )
642 {
643 CheckstyleReportGenerator generator = new CheckstyleReportGenerator( getSink(), bundle, project.getBasedir(), siteTool );
644
645 generator.setLog( getLog() );
646 generator.setEnableRulesSummary( enableRulesSummary );
647 generator.setEnableSeveritySummary( enableSeveritySummary );
648 generator.setEnableFilesSummary( enableFilesSummary );
649 generator.setEnableRSS( enableRSS );
650 generator.setCheckstyleConfig( results.getConfiguration() );
651 if ( linkXRef )
652 {
653 String relativePath = PathTool.getRelativePath( getOutputDirectory(), xrefLocation.getAbsolutePath() );
654 if ( StringUtils.isEmpty( relativePath ) )
655 {
656 relativePath = ".";
657 }
658 relativePath = relativePath + "/" + xrefLocation.getName();
659 if ( xrefLocation.exists() )
660 {
661
662
663 generator.setXrefLocation( relativePath );
664 }
665 else
666 {
667
668 for ( Iterator reports = getProject().getReportPlugins().iterator(); reports.hasNext(); )
669 {
670 ReportPlugin report = (ReportPlugin) reports.next();
671
672 String artifactId = report.getArtifactId();
673 if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
674 {
675 generator.setXrefLocation( relativePath );
676 }
677 }
678 }
679
680 if ( generator.getXrefLocation() == null )
681 {
682 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
683 }
684 }
685 generator.generateReport( results );
686 }
687
688
689
690
691
692
693
694 private void mergeDeprecatedInfo()
695 {
696 if ( "config/sun_checks.xml".equals( configLocation ) && !"sun".equals( format ) )
697 {
698 configLocation = (String) FORMAT_TO_CONFIG_LOCATION.get( format );
699 }
700
701 if ( StringUtils.isEmpty( propertiesLocation ) )
702 {
703 if ( propertiesFile != null )
704 {
705 propertiesLocation = propertiesFile.getPath();
706 }
707 else if ( propertiesURL != null )
708 {
709 propertiesLocation = propertiesURL.toExternalForm();
710 }
711 }
712
713 if ( "LICENSE.txt".equals( headerLocation ) )
714 {
715 File defaultHeaderFile = new File( project.getBasedir(), "LICENSE.txt" );
716 if ( !defaultHeaderFile.equals( headerFile ) )
717 {
718 headerLocation = headerFile.getPath();
719 }
720 }
721
722 if ( StringUtils.isEmpty( suppressionsLocation ) )
723 {
724 suppressionsLocation = suppressionsFile;
725 }
726
727 if ( StringUtils.isEmpty( packageNamesLocation ) )
728 {
729 packageNamesLocation = packageNamesFile;
730 }
731 }
732
733
734
735 public String getOutputName()
736 {
737 return "checkstyle";
738 }
739
740 private AuditListener getListener()
741 throws MavenReportException
742 {
743 AuditListener listener = null;
744
745 if ( StringUtils.isNotEmpty( outputFileFormat ) )
746 {
747 File resultFile = outputFile;
748
749 OutputStream out = getOutputStream( resultFile );
750
751 if ( "xml".equals( outputFileFormat ) )
752 {
753 listener = new XMLLogger( out, true );
754 }
755 else if ( "plain".equals( outputFileFormat ) )
756 {
757 listener = new DefaultLogger( out, true );
758 }
759 else
760 {
761
762 throw new MavenReportException( "Invalid output file format: (" + outputFileFormat
763 + "). Must be 'plain' or 'xml'." );
764 }
765 }
766
767 return listener;
768 }
769
770 private OutputStream getOutputStream( File file )
771 throws MavenReportException
772 {
773 File parentFile = file.getAbsoluteFile().getParentFile();
774
775 if ( !parentFile.exists() )
776 {
777 parentFile.mkdirs();
778 }
779
780 FileOutputStream fileOutputStream;
781 try
782 {
783 fileOutputStream = new FileOutputStream( file );
784 }
785 catch ( FileNotFoundException e )
786 {
787 throw new MavenReportException( "Unable to create output stream: " + file, e );
788 }
789 return fileOutputStream;
790 }
791
792 private DefaultLogger getConsoleListener()
793 throws MavenReportException
794 {
795 DefaultLogger consoleListener;
796
797 if ( useFile == null )
798 {
799 stringOutputStream = new ByteArrayOutputStream();
800 consoleListener = new DefaultLogger( stringOutputStream, false );
801 }
802 else
803 {
804 OutputStream out = getOutputStream( useFile );
805
806 consoleListener = new DefaultLogger( out, true );
807 }
808
809 return consoleListener;
810 }
811
812 private static ResourceBundle getBundle( Locale locale )
813 {
814 return ResourceBundle.getBundle( "checkstyle-report", locale, CheckstyleReport.class.getClassLoader() );
815 }
816
817
818 public boolean canGenerateReport()
819 {
820
821 return sourceDirectory.exists();
822 }
823
824
825 public void setReportOutputDirectory( File reportOutputDirectory )
826 {
827 super.setReportOutputDirectory( reportOutputDirectory );
828 this.outputDirectory = reportOutputDirectory;
829 }
830 }