View Javadoc
1   package org.apache.maven.plugins.checkstyle;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.configuration.PlexusConfiguration;
54  import org.codehaus.plexus.resource.ResourceManager;
55  import org.codehaus.plexus.resource.loader.FileResourceLoader;
56  import org.codehaus.plexus.util.FileUtils;
57  import org.codehaus.plexus.util.PathTool;
58  import org.codehaus.plexus.util.StringUtils;
59  
60  import com.puppycrawl.tools.checkstyle.DefaultLogger;
61  import com.puppycrawl.tools.checkstyle.XMLLogger;
62  import com.puppycrawl.tools.checkstyle.api.AuditListener;
63  import com.puppycrawl.tools.checkstyle.api.AutomaticBean.OutputStreamOptions;
64  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
65  
66  /**
67   * Base abstract class for Checkstyle reports.
68   *
69   *
70   */
71  public abstract class AbstractCheckstyleReport
72      extends AbstractMavenReport
73  {
74      public static final String PLUGIN_RESOURCES = "org/apache/maven/plugins/checkstyle";
75  
76      protected static final String JAVA_FILES = "**\\/*.java";
77  
78      private static final String DEFAULT_CONFIG_LOCATION = "sun_checks.xml";
79  
80      /**
81       * Specifies the cache file used to speed up Checkstyle on successive runs.
82       */
83      @Parameter( defaultValue = "${project.build.directory}/checkstyle-cachefile" )
84      protected String cacheFile;
85  
86      /**
87       * <p>
88       * Specifies the location of the XML configuration to use.
89       * <p>
90       * Potential values are a filesystem path, a URL, or a classpath resource.
91       * This parameter expects that the contents of the location conform to the
92       * xml format (Checkstyle <a
93       * href="http://checkstyle.sourceforge.net/config.html#Modules">Checker
94       * module</a>) configuration of rulesets.
95       * <p>
96       * This parameter is resolved as resource, URL, then file. If successfully
97       * resolved, the contents of the configuration is copied into the
98       * <code>${project.build.directory}/checkstyle-configuration.xml</code>
99       * file before being passed to Checkstyle as a configuration.
100      * <p>
101      * There are 2 predefined rulesets included in Maven Checkstyle Plugin:
102      * <ul>
103      * <li><code>sun_checks.xml</code>: Sun Checks.</li>
104      * <li><code>google_checks.xml</code>: Google Checks.</li>
105      * </ul>
106      */
107     @Parameter( property = "checkstyle.config.location", defaultValue = DEFAULT_CONFIG_LOCATION )
108     protected String configLocation;
109 
110     /**
111      * Output errors to console.
112      */
113     @Parameter( property = "checkstyle.consoleOutput", defaultValue = "false" )
114     protected boolean consoleOutput;
115 
116     /**
117      * The file encoding to use when reading the source files. If the property <code>project.build.sourceEncoding</code>
118      * is not set, the platform default encoding is used. <strong>Note:</strong> This parameter always overrides the
119      * property <code>charset</code> from Checkstyle's <code>TreeWalker</code> module.
120      *
121      * @since 2.2
122      */
123     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
124     protected String encoding;
125 
126     /**
127      * Specifies if the build should fail upon a violation.
128      */
129     @Parameter( defaultValue = "false" )
130     protected boolean failsOnError;
131 
132     /**
133      * <p>
134      * Specifies the location of the License file (a.k.a. the header file) that
135      * can be used by Checkstyle to verify that source code has the correct
136      * license header.
137      * <p>
138      * You need to use <code>${checkstyle.header.file}</code> in your Checkstyle xml
139      * configuration to reference the name of this header file.
140      * <p>
141      * For instance:
142      * <pre>
143      * &lt;module name="RegexpHeader"&gt;
144      *   &lt;property name="headerFile" value="${checkstyle.header.file}"/&gt;
145      * &lt;/module&gt;
146      * </pre>
147      *
148      * @since 2.0-beta-2
149      */
150     @Parameter( property = "checkstyle.header.file", defaultValue = "LICENSE.txt" )
151     protected String headerLocation;
152 
153     /**
154      * Skip entire check.
155      *
156      * @since 2.2
157      */
158     @Parameter( property = "checkstyle.skip", defaultValue = "false" )
159     protected boolean skip;
160 
161     /**
162      * Specifies the path and filename to save the Checkstyle output. The format
163      * of the output file is determined by the <code>outputFileFormat</code>
164      * parameter.
165      */
166     @Parameter( property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml" )
167     private File outputFile;
168 
169     /**
170      * <p>
171      * Specifies the location of the properties file.
172      * <p>
173      * This parameter is resolved as URL, File then resource. If successfully
174      * resolved, the contents of the properties location is copied into the
175      * <code>${project.build.directory}/checkstyle-checker.properties</code>
176      * file before being passed to Checkstyle for loading.
177      * <p>
178      * The contents of the <code>propertiesLocation</code> will be made
179      * available to Checkstyle for specifying values for parameters within the
180      * xml configuration (specified in the <code>configLocation</code>
181      * parameter).
182      *
183      * @since 2.0-beta-2
184      */
185     @Parameter( property = "checkstyle.properties.location" )
186     protected String propertiesLocation;
187 
188     /**
189      * Allows for specifying raw property expansion information.
190      */
191     @Parameter
192     protected String propertyExpansion;
193 
194     /**
195      * Specifies the location of the resources to be used for Checkstyle.
196      *
197      * @since 2.10
198      */
199     @Parameter( defaultValue = "${project.resources}", readonly = true )
200     protected List<Resource> resources;
201 
202     /**
203      * Specifies the location of the test resources to be used for Checkstyle.
204      *
205      * @since 2.11
206      */
207     @Parameter( defaultValue = "${project.testResources}", readonly = true )
208     protected List<Resource> testResources;
209 
210     /**
211      * Specifies the names filter of the source files to be used for Checkstyle.
212      */
213     @Parameter( property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true )
214     protected String includes;
215 
216     /**
217      * Specifies the names filter of the source files to be excluded for
218      * Checkstyle.
219      */
220     @Parameter( property = "checkstyle.excludes" )
221     protected String excludes;
222 
223     /**
224      * Specifies the names filter of the resource files to be used for Checkstyle.
225      * @since 2.11
226      */
227     @Parameter( property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true )
228     protected String resourceIncludes;
229 
230     /**
231      * Specifies the names filter of the resource files to be excluded for
232      * Checkstyle.
233      * @since 2.11
234      */
235     @Parameter( property = "checkstyle.resourceExcludes" )
236     protected String resourceExcludes;
237 
238     /**
239      * Specifies whether to include the resource directories in the check.
240      * @since 2.11
241      */
242     @Parameter( property = "checkstyle.includeResources", defaultValue = "true", required = true )
243     protected boolean includeResources;
244 
245     /**
246      * Specifies whether to include the test resource directories in the check.
247      * @since 2.11
248      */
249     @Parameter( property = "checkstyle.includeTestResources", defaultValue = "true", required = true )
250     protected boolean includeTestResources;
251 
252     /**
253      * Specifies the location of the source directory to be used for Checkstyle.
254      * 
255      * @deprecated instead use {@link #sourceDirectories}. For version 3.0.0, this parameter is only defined to break
256      *             the build if you use it!
257      */
258     @Deprecated
259     @Parameter
260     private File sourceDirectory;
261 
262     /**
263      * Specifies the location of the source directories to be used for Checkstyle.
264      * Default value is <code>${project.compileSourceRoots}</code>.
265      * @since 2.13
266      */
267     // Compatibility with all Maven 3: default of 'project.compileSourceRoots' is done manually because of MNG-5440
268     @Parameter
269     private List<String> sourceDirectories;
270     
271     /**
272      * Specifies the location of the test source directory to be used for Checkstyle.
273      *
274      * @since 2.2
275      * @deprecated instead use {@link #testSourceDirectories}. For version 3.0.0, this parameter is only defined to
276      *             break the build if you use it!
277      */
278     @Parameter
279     @Deprecated
280     private File testSourceDirectory;
281     
282     /**
283      * Specifies the location of the test source directories to be used for Checkstyle.
284      * Default value is <code>${project.testCompileSourceRoots}</code>.
285      * @since 2.13
286      */
287     // Compatibility with all Maven 3: default of 'project.testCompileSourceRoots' is done manually because of MNG-5440
288     @Parameter
289     private List<String> testSourceDirectories;
290 
291     /**
292      * Include or not the test source directory/directories to be used for Checkstyle.
293      *
294      * @since 2.2
295      */
296     @Parameter( defaultValue = "false" )
297     protected boolean includeTestSourceDirectory;
298 
299     /**
300      * The key to be used in the properties for the suppressions file.
301      *
302      * @since 2.1
303      */
304     @Parameter( property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file" )
305     protected String suppressionsFileExpression;
306 
307     /**
308      * <p>
309      * Specifies the location of the suppressions XML file to use.
310      * <p>
311      * This parameter is resolved as resource, URL, then file. If successfully
312      * resolved, the contents of the suppressions XML is copied into the
313      * <code>${project.build.directory}/checkstyle-supressions.xml</code> file
314      * before being passed to Checkstyle for loading.
315      * <p>
316      * See <code>suppressionsFileExpression</code> for the property that will
317      * be made available to your Checkstyle configuration.
318      *
319      * @since 2.0-beta-2
320      */
321     @Parameter( property = "checkstyle.suppressions.location" )
322     protected String suppressionsLocation;
323 
324     /**
325      * If <code>null</code>, the Checkstyle plugin will display violations on stdout.
326      * Otherwise, a text file will be created with the violations.
327      */
328     @Parameter
329     private File useFile;
330 
331     /**
332      * Specifies the format of the output to be used when writing to the output
333      * file. Valid values are "<code>plain</code>" and "<code>xml</code>".
334      */
335     @Parameter( property = "checkstyle.output.format", defaultValue = "xml" )
336     private String outputFileFormat;
337 
338     /**
339      * Specifies if the Rules summary should be enabled or not.
340      */
341     @Parameter( property = "checkstyle.enable.rules.summary", defaultValue = "true" )
342     private boolean enableRulesSummary;
343 
344     /**
345      * Specifies if the Severity summary should be enabled or not.
346      */
347     @Parameter( property = "checkstyle.enable.severity.summary", defaultValue = "true" )
348     private boolean enableSeveritySummary;
349 
350     /**
351      * Specifies if the Files summary should be enabled or not.
352      */
353     @Parameter( property = "checkstyle.enable.files.summary", defaultValue = "true" )
354     private boolean enableFilesSummary;
355 
356     /**
357      * Specifies if the RSS should be enabled or not.
358      */
359     @Parameter( property = "checkstyle.enable.rss", defaultValue = "true" )
360     private boolean enableRSS;
361 
362     /**
363      * SiteTool.
364      *
365      * @since 2.2
366      */
367     @Component( role = SiteTool.class )
368     protected SiteTool siteTool;
369 
370     /**
371      * The Plugin Descriptor
372      */
373     @Parameter( defaultValue = "${plugin}", readonly = true, required = true )
374     private PluginDescriptor plugin;
375 
376     /**
377      * Link the violation line numbers to the source xref. Will link
378      * automatically if Maven JXR plugin is being used.
379      *
380      * @since 2.1
381      */
382     @Parameter( property = "linkXRef", defaultValue = "true" )
383     private boolean linkXRef;
384 
385     /**
386      * Location of the Xrefs to link to.
387      */
388     @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref" )
389     private File xrefLocation;
390 
391     /**
392      * When using custom treeWalkers, specify their names here so the checks
393      * inside the treeWalker end up the the rule-summary.
394      * 
395      * @since 2.11
396      */
397     @Parameter
398     private List<String> treeWalkerNames;
399 
400     /**
401      * Specifies whether modules with a configured severity of <code>ignore</code> should be omitted during Checkstyle
402      * invocation.
403      * 
404      * @since 3.0.0
405      */
406     @Parameter( defaultValue = "false" )
407     private boolean omitIgnoredModules;
408 
409     /**
410      * By using this property, you can specify the whole Checkstyle rules
411      * inline directly inside this pom.
412      *
413      * <pre>
414      * &lt;plugin&gt;
415      *   ...
416      *   &lt;configuration&gt;
417      *     &lt;checkstyleRules&gt;
418      *       &lt;module name="Checker"&gt;
419      *         &lt;module name="FileTabCharacter"&gt;
420      *           &lt;property name="eachLine" value="true" /&gt;
421      *         &lt;/module&gt;
422      *         &lt;module name="TreeWalker"&gt;
423      *           &lt;module name="EmptyBlock"/&gt;
424      *         &lt;/module&gt;
425      *       &lt;/module&gt;
426      *     &lt;/checkstyleRules&gt;
427      *   &lt;/configuration&gt;
428      *   ...
429      * </pre>
430      *
431      * @since 2.12
432      */
433     @Parameter
434     private PlexusConfiguration checkstyleRules;
435 
436     /**
437      * Dump file for inlined Checkstyle rules.
438      */
439     @Parameter( property = "checkstyle.output.rules.file",
440                     defaultValue = "${project.build.directory}/checkstyle-rules.xml" )
441     private File rulesFiles;
442 
443     /**
444      * The header to use for the inline configuration.
445      * Only used when you specify {@code checkstyleRules}.
446      */
447     @Parameter( defaultValue = "<?xml version=\"1.0\"?>\n"
448             + "<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n"
449             + "        \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n" )
450     private String checkstyleRulesHeader;
451 
452     /**
453      */
454     @Component
455     protected ResourceManager locator;
456 
457     /**
458      * CheckstyleRssGenerator.
459      *
460      * @since 2.4
461      */
462     @Component( role = CheckstyleRssGenerator.class, hint = "default" )
463     protected CheckstyleRssGenerator checkstyleRssGenerator;
464 
465     /**
466      * @since 2.5
467      */
468     @Component( role = CheckstyleExecutor.class, hint = "default" )
469     protected CheckstyleExecutor checkstyleExecutor;
470 
471     protected ByteArrayOutputStream stringOutputStream;
472 
473     /** {@inheritDoc} */
474     public String getName( Locale locale )
475     {
476         return getBundle( locale ).getString( "report.checkstyle.name" );
477     }
478 
479     /** {@inheritDoc} */
480     public String getDescription( Locale locale )
481     {
482         return getBundle( locale ).getString( "report.checkstyle.description" );
483     }
484 
485     /** {@inheritDoc} */
486     public void executeReport( Locale locale )
487         throws MavenReportException
488     {
489         checkDeprecatedParameterUsage( sourceDirectory, "sourceDirectory", "sourceDirectories" );
490         checkDeprecatedParameterUsage( testSourceDirectory, "testSourceDirectory", "testSourceDirectories" );
491 
492         locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
493         locator.addSearchPath( "url", "" );
494 
495         locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
496 
497         // for when we start using maven-shared-io and maven-shared-monitor...
498         // locator = new Locator( new MojoLogMonitorAdaptor( getLog() ) );
499 
500         // locator = new Locator( getLog(), new File( project.getBuild().getDirectory() ) );
501         if ( checkstyleRules != null )
502         {
503             if ( !DEFAULT_CONFIG_LOCATION.equals( configLocation ) )
504             {
505                 throw new MavenReportException( "If you use inline configuration for rules, don't specify "
506                         + "a configLocation" );
507             }
508             if ( checkstyleRules.getChildCount() > 1 )
509             {
510                 throw new MavenReportException( "Currently only one root module is supported" );
511             }
512             PlexusConfiguration checkerModule = checkstyleRules.getChild( 0 );
513 
514             try
515             {
516                 FileUtils.forceMkdir( rulesFiles.getParentFile() );
517                 FileUtils.fileWrite( rulesFiles, checkstyleRulesHeader + checkerModule.toString() );
518             }
519             catch ( final IOException e )
520             {
521                 throw new MavenReportException( e.getMessage(), e );
522             }
523             configLocation = rulesFiles.getAbsolutePath();
524         }
525         ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
526 
527         try
528         {
529             CheckstyleExecutorRequest request = createRequest().setLicenseArtifacts( collectArtifacts( "license" ) )
530                             .setConfigurationArtifacts( collectArtifacts( "configuration" ) )
531                             .setOmitIgnoredModules( omitIgnoredModules );
532 
533             CheckstyleResults results = checkstyleExecutor.executeCheckstyle( request );
534 
535             ResourceBundle bundle = getBundle( locale );
536             generateReportStatics();
537             generateMainReport( results, bundle );
538             if ( enableRSS )
539             {
540                 CheckstyleRssGeneratorRequest checkstyleRssGeneratorRequest =
541                     new CheckstyleRssGeneratorRequest( this.project, this.getCopyright(), outputDirectory, getLog() );
542                 checkstyleRssGenerator.generateRSS( results, checkstyleRssGeneratorRequest );
543             }
544 
545         }
546         catch ( CheckstyleException e )
547         {
548             throw new MavenReportException( "Failed during checkstyle configuration", e );
549         }
550         catch ( CheckstyleExecutorException e )
551         {
552             throw new MavenReportException( "Failed during checkstyle execution", e );
553         }
554         finally
555         {
556             //be sure to restore original context classloader
557             Thread.currentThread().setContextClassLoader( currentClassLoader );
558         }
559     }
560 
561     private void checkDeprecatedParameterUsage( Object parameter, String name, String replacement )
562         throws MavenReportException
563     {
564         if ( parameter != null )
565         {
566             throw new MavenReportException( "You are using '" + name + "' which has been removed"
567                 + " from the maven-checkstyle-plugin. " + "Please use '" + replacement
568                 + "' and refer to the >>Major Version Upgrade to version 3.0.0<< " + "on the plugin site." );
569         }
570     }
571 
572     /**
573      * Create the Checkstyle executor request.
574      *
575      * @return The executor request.
576      * @throws MavenReportException If something goes wrong during creation.
577      */
578     protected abstract CheckstyleExecutorRequest createRequest()
579             throws MavenReportException;
580 
581     private List<Artifact> collectArtifacts( String hint )
582     {
583         List<Artifact> artifacts = new ArrayList<>();
584 
585         PluginManagement pluginManagement = project.getBuild().getPluginManagement();
586         if ( pluginManagement != null )
587         {
588             artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( pluginManagement.getPluginsAsMap(), hint ) );
589         }
590 
591         artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( project.getBuild().getPluginsAsMap(), hint ) );
592 
593         return artifacts;
594     }
595 
596     private List<Artifact> getCheckstylePluginDependenciesAsArtifacts( Map<String, Plugin> plugins, String hint )
597     {
598         List<Artifact> artifacts = new ArrayList<>();
599         
600         Plugin checkstylePlugin = plugins.get( plugin.getGroupId() + ":" + plugin.getArtifactId() );
601         if ( checkstylePlugin != null )
602         {
603             for ( Dependency dep : checkstylePlugin.getDependencies() )
604             {
605              // @todo if we can filter on hints, it should be done here...
606                 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
607                 artifacts.add( plugin.getArtifactMap().get( depKey ) );
608             }
609         }
610         return artifacts;
611     }
612 
613     /**
614      * Creates and returns the report generation listener.
615      *
616      * @return The audit listener.
617      * @throws MavenReportException If something goes wrong.
618      */
619     protected AuditListener getListener()
620         throws MavenReportException
621     {
622         AuditListener listener = null;
623 
624         if ( StringUtils.isNotEmpty( outputFileFormat ) )
625         {
626             File resultFile = outputFile;
627 
628             OutputStream out = getOutputStream( resultFile );
629 
630             if ( "xml".equals( outputFileFormat ) )
631             {
632                 listener = new XMLLogger( out, OutputStreamOptions.CLOSE );
633             }
634             else if ( "plain".equals( outputFileFormat ) )
635             {
636                 listener = new DefaultLogger( out, OutputStreamOptions.CLOSE );
637             }
638             else
639             {
640                 // TODO: failure if not a report
641                 throw new MavenReportException( "Invalid output file format: (" + outputFileFormat
642                     + "). Must be 'plain' or 'xml'." );
643             }
644         }
645 
646         return listener;
647     }
648 
649     private OutputStream getOutputStream( File file )
650         throws MavenReportException
651     {
652         File parentFile = file.getAbsoluteFile().getParentFile();
653 
654         if ( !parentFile.exists() )
655         {
656             parentFile.mkdirs();
657         }
658 
659         FileOutputStream fileOutputStream;
660         try
661         {
662             fileOutputStream = new FileOutputStream( file );
663         }
664         catch ( FileNotFoundException e )
665         {
666             throw new MavenReportException( "Unable to create output stream: " + file, e );
667         }
668         return fileOutputStream;
669     }
670 
671     /**
672      * Creates and returns the console listener.
673      *
674      * @return The console listener.
675      * @throws MavenReportException If something goes wrong.
676      */
677     protected DefaultLogger getConsoleListener()
678         throws MavenReportException
679     {
680         DefaultLogger consoleListener;
681 
682         if ( useFile == null )
683         {
684             stringOutputStream = new ByteArrayOutputStream();
685             consoleListener = new DefaultLogger( stringOutputStream, OutputStreamOptions.NONE );
686         }
687         else
688         {
689             OutputStream out = getOutputStream( useFile );
690 
691             consoleListener = new DefaultLogger( out, OutputStreamOptions.CLOSE );
692         }
693 
694         return consoleListener;
695     }
696 
697     private void generateReportStatics()
698         throws MavenReportException
699     {
700         ReportResource rresource = new ReportResource( PLUGIN_RESOURCES, outputDirectory );
701         try
702         {
703             rresource.copy( "images/rss.png" );
704         }
705         catch ( IOException e )
706         {
707             throw new MavenReportException( "Unable to copy static resources.", e );
708         }
709     }
710 
711 
712     private String getCopyright()
713     {
714         String copyright;
715         int currentYear = Calendar.getInstance().get( Calendar.YEAR );
716         if ( StringUtils.isNotEmpty( project.getInceptionYear() )
717             && !String.valueOf( currentYear ).equals( project.getInceptionYear() ) )
718         {
719             copyright = project.getInceptionYear() + " - " + currentYear;
720         }
721         else
722         {
723             copyright = String.valueOf( currentYear );
724         }
725 
726         if ( ( project.getOrganization() != null ) && StringUtils.isNotEmpty( project.getOrganization().getName() ) )
727         {
728             copyright = copyright + " " + project.getOrganization().getName();
729         }
730         return copyright;
731     }
732 
733     private void generateMainReport( CheckstyleResults results, ResourceBundle bundle )
734     {
735         CheckstyleReportGenerator generator =
736             new CheckstyleReportGenerator( getSink(), bundle, project.getBasedir(), siteTool, configLocation );
737 
738         generator.setLog( getLog() );
739         generator.setEnableRulesSummary( enableRulesSummary );
740         generator.setEnableSeveritySummary( enableSeveritySummary );
741         generator.setEnableFilesSummary( enableFilesSummary );
742         generator.setEnableRSS( enableRSS );
743         generator.setCheckstyleConfig( results.getConfiguration() );
744         if ( linkXRef )
745         {
746             String relativePath = PathTool.getRelativePath( getOutputDirectory(), xrefLocation.getAbsolutePath() );
747             if ( StringUtils.isEmpty( relativePath ) )
748             {
749                 relativePath = ".";
750             }
751             relativePath = relativePath + "/" + xrefLocation.getName();
752             if ( xrefLocation.exists() )
753             {
754                 // XRef was already generated by manual execution of a lifecycle
755                 // binding
756                 generator.setXrefLocation( relativePath );
757             }
758             else
759             {
760                 // Not yet generated - check if the report is on its way
761                 for ( ReportPlugin report : (Iterable<ReportPlugin>) getProject().getReportPlugins() )
762                 {
763                     String artifactId = report.getArtifactId();
764                     if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
765                     {
766                         generator.setXrefLocation( relativePath );
767                     }
768                 }
769             }
770 
771             if ( generator.getXrefLocation() == null && results.getFileCount() > 0 )
772             {
773                 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
774             }
775         }
776         if ( treeWalkerNames != null )
777         {
778             generator.setTreeWalkerNames( treeWalkerNames );
779         }
780         generator.generateReport( results );
781     }
782 
783     private static ResourceBundle getBundle( Locale locale )
784     {
785         return ResourceBundle.getBundle( "checkstyle-report", locale, AbstractCheckstyleReport.class.getClassLoader() );
786     }
787 
788     protected List<File> getSourceDirectories()
789     {
790         if ( sourceDirectories == null )
791         {
792             sourceDirectories = project.getCompileSourceRoots();
793         }
794         List<File> sourceDirs = new ArrayList<>( sourceDirectories.size() );
795         for ( String sourceDir : sourceDirectories )
796         {
797             sourceDirs.add( FileUtils.resolveFile( project.getBasedir(), sourceDir ) );
798         }
799         return sourceDirs;
800     }
801 
802     protected List<File> getTestSourceDirectories()
803     {
804         if ( testSourceDirectories == null )
805         {
806             testSourceDirectories = project.getTestCompileSourceRoots();
807         }
808         List<File> testSourceDirs = new ArrayList<>( testSourceDirectories.size() );
809         for ( String testSourceDir : testSourceDirectories )
810         {
811             testSourceDirs.add( FileUtils.resolveFile( project.getBasedir(), testSourceDir ) );
812         }
813         return testSourceDirs;
814     }
815 }