View Javadoc

1   package org.apache.maven.plugin.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 com.puppycrawl.tools.checkstyle.Checker;
23  import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
24  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
25  import com.puppycrawl.tools.checkstyle.DefaultLogger;
26  import com.puppycrawl.tools.checkstyle.ModuleFactory;
27  import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
28  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
29  import com.puppycrawl.tools.checkstyle.XMLLogger;
30  import com.puppycrawl.tools.checkstyle.api.AuditListener;
31  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
32  import com.puppycrawl.tools.checkstyle.api.Configuration;
33  import com.puppycrawl.tools.checkstyle.api.FilterSet;
34  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
35  import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader;
36  import org.apache.maven.artifact.DependencyResolutionRequiredException;
37  import org.apache.maven.doxia.tools.SiteTool;
38  import org.apache.maven.model.ReportPlugin;
39  import org.apache.maven.plugin.MojoExecutionException;
40  import org.apache.maven.project.MavenProject;
41  import org.apache.maven.reporting.AbstractMavenReport;
42  import org.apache.maven.reporting.MavenReportException;
43  import org.apache.velocity.VelocityContext;
44  import org.apache.velocity.context.Context;
45  import org.apache.velocity.exception.ResourceNotFoundException;
46  import org.apache.velocity.exception.VelocityException;
47  import org.codehaus.doxia.site.renderer.SiteRenderer;
48  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
49  import org.codehaus.plexus.personality.plexus.lifecycle.phase.ServiceLocator;
50  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Serviceable;
51  import org.codehaus.plexus.resource.ResourceManager;
52  import org.codehaus.plexus.resource.loader.FileResourceCreationException;
53  import org.codehaus.plexus.resource.loader.FileResourceLoader;
54  import org.codehaus.plexus.util.FileUtils;
55  import org.codehaus.plexus.util.PathTool;
56  import org.codehaus.plexus.util.StringInputStream;
57  import org.codehaus.plexus.util.StringOutputStream;
58  import org.codehaus.plexus.util.StringUtils;
59  import org.codehaus.plexus.velocity.VelocityComponent;
60  
61  import java.io.File;
62  import java.io.FileInputStream;
63  import java.io.FileNotFoundException;
64  import java.io.FileOutputStream;
65  import java.io.IOException;
66  import java.io.OutputStream;
67  import java.net.MalformedURLException;
68  import java.net.URL;
69  import java.net.URLClassLoader;
70  import java.util.ArrayList;
71  import java.util.Calendar;
72  import java.util.Collections;
73  import java.util.HashMap;
74  import java.util.Iterator;
75  import java.util.List;
76  import java.util.Locale;
77  import java.util.Map;
78  import java.util.Properties;
79  import java.util.ResourceBundle;
80  
81  /**
82   * Perform a Checkstyle analysis, and generate a report on violations.
83   *
84   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
85   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
86   * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
87   * @goal checkstyle
88   * @requiresDependencyResolution compile
89   */
90  public class CheckstyleReport
91      extends AbstractMavenReport
92      implements Serviceable
93  {
94      private static final String PLUGIN_RESOURCES = "org/apache/maven/plugin/checkstyle";
95  
96      /**
97       * @deprecated Remove with format parameter.
98       */
99      private static final Map FORMAT_TO_CONFIG_LOCATION;
100 
101     static
102     {
103         Map fmt2Cfg = new HashMap();
104 
105         fmt2Cfg.put( "sun", "config/sun_checks.xml" );
106         fmt2Cfg.put( "turbine", "config/turbine_checks.xml" );
107         fmt2Cfg.put( "avalon", "config/avalon_checks.xml" );
108         fmt2Cfg.put( "maven", "config/maven_checks.xml" );
109 
110         FORMAT_TO_CONFIG_LOCATION = Collections.unmodifiableMap( fmt2Cfg );
111     }
112 
113     /**
114      * Skip entire check.
115      *
116      * @parameter expression="${checkstyle.skip}" default-value="false"
117      * @since 2.2
118      */
119     private boolean skip;
120 
121     /**
122      * The output directory for the report. Note that this parameter is only
123      * evaluated if the goal is run directly from the command line. If the goal
124      * is run indirectly as part of a site generation, the output directory
125      * configured in Maven Site Plugin is used instead.
126      *
127      * @parameter default-value="${project.reporting.outputDirectory}"
128      * @required
129      */
130     private File outputDirectory;
131 
132     /**
133      * Specifies if the Rules summary should be enabled or not.
134      *
135      * @parameter expression="${checkstyle.enable.rules.summary}"
136      *            default-value="true"
137      */
138     private boolean enableRulesSummary;
139 
140     /**
141      * Specifies if the Severity summary should be enabled or not.
142      *
143      * @parameter expression="${checkstyle.enable.severity.summary}"
144      *            default-value="true"
145      */
146     private boolean enableSeveritySummary;
147 
148     /**
149      * Specifies if the Files summary should be enabled or not.
150      *
151      * @parameter expression="${checkstyle.enable.files.summary}"
152      *            default-value="true"
153      */
154     private boolean enableFilesSummary;
155 
156     /**
157      * Specifies if the RSS should be enabled or not.
158      *
159      * @parameter expression="${checkstyle.enable.rss}" default-value="true"
160      */
161     private boolean enableRSS;
162 
163     /**
164      * Specifies the names filter of the source files to be used for Checkstyle.
165      *
166      * @parameter expression="${checkstyle.includes}" default-value="**\/*.java"
167      * @required
168      */
169     private String includes;
170 
171     /**
172      * Specifies the names filter of the source files to be excluded for
173      * Checkstyle.
174      *
175      * @parameter expression="${checkstyle.excludes}"
176      */
177     private String excludes;
178 
179     /**
180      * <p>
181      * Specifies the location of the XML configuration to use.
182      * </p>
183      *
184      * <p>
185      * Potential values are a filesystem path, a URL, or a classpath resource.
186      * This parameter expects that the contents of the location conform to the
187      * xml format (Checkstyle <a
188      * href="http://checkstyle.sourceforge.net/config.html#Modules">Checker
189      * module</a>) configuration of rulesets.
190      * </p>
191      *
192      * <p>
193      * This parameter is resolved as resource, URL, then file. If successfully
194      * resolved, the contents of the configuration is copied into the
195      * <code>${project.build.directory}/checkstyle-configuration.xml</code>
196      * file before being passed to Checkstyle as a configuration.
197      * </p>
198      *
199      * <p>
200      * There are 4 predefined rulesets.
201      * </p>
202      *
203      * <ul>
204      * <li><code>config/sun_checks.xml</code>: Sun Checks.</li>
205      * <li><code>config/turbine_checks.xml</code>: Turbine Checks.</li>
206      * <li><code>config/avalon_checks.xml</code>: Avalon Checks.</li>
207      * <li><code>config/maven_checks.xml</code>: Maven Source Checks.</li>
208      * </ul>
209      *
210      * @parameter expression="${checkstyle.config.location}"
211      *            default-value="config/sun_checks.xml"
212      */
213     private String configLocation;
214 
215     /**
216      * Specifies what predefined check set to use. Available sets are "sun" (for
217      * the Sun coding conventions), "turbine", and "avalon".
218      *
219      * @parameter default-value="sun"
220      * @deprecated Use configLocation instead.
221      */
222     private String format;
223 
224     /**
225      * <p>
226      * Specifies the location of the properties file.
227      * </p>
228      *
229      * <p>
230      * This parameter is resolved as URL, File then resource. If successfully
231      * resolved, the contents of the properties location is copied into the
232      * <code>${project.build.directory}/checkstyle-checker.properties</code>
233      * file before being passed to Checkstyle for loading.
234      * </p>
235      *
236      * <p>
237      * The contents of the <code>propertiesLocation</code> will be made
238      * available to Checkstyle for specifying values for parameters within the
239      * xml configuration (specified in the <code>configLocation</code>
240      * parameter).
241      * </p>
242      *
243      * @parameter expression="${checkstyle.properties.location}"
244      * @since 2.0-beta-2
245      */
246     private String propertiesLocation;
247 
248     /**
249      * Specifies the location of the Checkstyle properties file that will be used to
250      * check the source.
251      *
252      * @parameter
253      * @deprecated Use propertiesLocation instead.
254      */
255     private File propertiesFile;
256 
257     /**
258      * Specifies the URL of the Checkstyle properties that will be used to check
259      * the source.
260      *
261      * @parameter
262      * @deprecated Use propertiesLocation instead.
263      */
264     private URL propertiesURL;
265 
266     /**
267      * Allows for specifying raw property expansion information.
268      *
269      * @parameter
270      */
271     private String propertyExpansion;
272 
273     /**
274      * <p>
275      * Specifies the location of the License file (a.k.a. the header file) that
276      * can be used by Checkstyle to verify that source code has the correct
277      * license header.
278      * </p>
279      * <p>
280      * You need to use ${checkstyle.header.file} in your Checkstyle xml
281      * configuration to reference the name of this header file.
282      * </p>
283      * <p>
284      * For instance:
285      * </p>
286      * <p>
287      * <code>
288      * &lt;module name="RegexpHeader">
289      *   &lt;property name="headerFile" value="${checkstyle.header.file}"/>
290      * &lt;/module>
291      * </code>
292      * </p>
293      *
294      * @parameter expression="${checkstyle.header.file}"
295      *            default-value="LICENSE.txt"
296      * @since 2.0-beta-2
297      */
298     private String headerLocation;
299 
300     /**
301      * Specifies the location of the License file (a.k.a. the header file) that
302      * is used by Checkstyle to verify that source code has the correct
303      * license header.
304      *
305      * @parameter expression="${basedir}/LICENSE.txt"
306      * @deprecated Use headerLocation instead.
307      */
308     private File headerFile;
309 
310     /**
311      * Specifies the cache file used to speed up Checkstyle on successive runs.
312      *
313      * @parameter default-value="${project.build.directory}/checkstyle-cachefile"
314      */
315     private String cacheFile;
316 
317     /**
318      * If <code>null</code>, the Checkstyle plugin will display violations on stdout.
319      * Otherwise, a text file will be created with the violations.
320      *
321      * @parameter
322      */
323     private File useFile;
324 
325     /**
326      * SiteTool.
327      *
328      * @since 2.2
329      * @component role="org.apache.maven.doxia.tools.SiteTool"
330      * @required
331      * @readonly
332      */
333     protected SiteTool siteTool;
334 
335     /**
336      * <p>
337      * Specifies the location of the suppressions XML file to use.
338      * </p>
339      *
340      * <p>
341      * This parameter is resolved as resource, URL, then file. If successfully
342      * resolved, the contents of the suppressions XML is copied into the
343      * <code>${project.build.directory}/checkstyle-supressions.xml</code> file
344      * before being passed to Checkstyle for loading.
345      * </p>
346      *
347      * <p>
348      * See <code>suppressionsFileExpression</code> for the property that will
349      * be made available to your checkstyle configuration.
350      * </p>
351      *
352      * @parameter expression="${checkstyle.suppressions.location}"
353      * @since 2.0-beta-2
354      */
355     private String suppressionsLocation;
356 
357     /**
358      * The key to be used in the properties for the suppressions file.
359      *
360      * @parameter expression="${checkstyle.suppression.expression}"
361      *            default-value="checkstyle.suppressions.file"
362      * @since 2.1
363      */
364     private String suppressionsFileExpression;
365 
366     /**
367      * Specifies the location of the suppressions XML file to use. The plugin
368      * defines a Checkstyle property named
369      * <code>checkstyle.suppressions.file</code> with the value of this
370      * property. This allows using the Checkstyle property in your own custom
371      * checkstyle configuration file when specifying a suppressions file.
372      *
373      * @parameter
374      * @deprecated Use suppressionsLocation instead.
375      */
376     private String suppressionsFile;
377 
378     /**
379      * Specifies the path and filename to save the checkstyle output. The format
380      * of the output file is determined by the <code>outputFileFormat</code>
381      * parameter.
382      *
383      * @parameter expression="${checkstyle.output.file}"
384      *            default-value="${project.build.directory}/checkstyle-result.xml"
385      */
386     private File outputFile;
387 
388     /**
389      * Specifies the format of the output to be used when writing to the output
390      * file. Valid values are "plain" and "xml".
391      *
392      * @parameter expression="${checkstyle.output.format}" default-value="xml"
393      */
394     private String outputFileFormat;
395 
396     /**
397      * <p>
398      * Specifies the location of the package names XML to be used to configure
399      * the Checkstyle <a
400      * href="http://checkstyle.sourceforge.net/config.html#Packages">Packages</a>.
401      * </p>
402      *
403      * <p>
404      * This parameter is resolved as resource, URL, then file. If resolved to a
405      * resource, or a URL, the contents of the package names XML is copied into
406      * the <code>${project.build.directory}/checkstyle-packagenames.xml</code>
407      * file before being passed to Checkstyle for loading.
408      * </p>
409      *
410      * @parameter
411      * @since 2.0-beta-2
412      */
413     private String packageNamesLocation;
414 
415     /**
416      * Specifies the location of the package names XML to be used to configure
417      * Checkstyle.
418      *
419      * @parameter
420      * @deprecated Use packageNamesLocation instead.
421      */
422     private String packageNamesFile;
423 
424     /**
425      * Specifies if the build should fail upon a violation.
426      *
427      * @parameter default-value="false"
428      */
429     private boolean failsOnError;
430 
431     /**
432      * Specifies the location of the source directory to be used for Checkstyle.
433      *
434      * @parameter default-value="${project.build.sourceDirectory}"
435      * @required
436      */
437     private File sourceDirectory;
438 
439     /**
440      * Specifies the location of the test source directory to be used for
441      * Checkstyle.
442      *
443      * @parameter default-value="${project.build.testSourceDirectory}"
444      * @since 2.2
445      */
446     private File testSourceDirectory;
447 
448     /**
449      * Include or not the test source directory to be used for Checkstyle.
450      *
451      * @parameter default-value="${false}"
452      * @since 2.2
453      */
454     private boolean includeTestSourceDirectory;
455 
456     /**
457      * The Maven Project Object.
458      *
459      * @parameter default-value="${project}"
460      * @required
461      * @readonly
462      */
463     private MavenProject project;
464 
465     /**
466      * Output errors to console.
467      *
468      * @parameter default-value="false"
469      */
470     private boolean consoleOutput;
471 
472     /**
473      * Link the violation line numbers to the source xref. Will link
474      * automatically if Maven JXR plugin is being used.
475      *
476      * @parameter expression="${linkXRef}" default-value="true"
477      * @since 2.1
478      */
479     private boolean linkXRef;
480 
481     /**
482      * Location of the Xrefs to link to.
483      *
484      * @parameter default-value="${project.reporting.outputDirectory}/xref"
485      */
486     private File xrefLocation;
487 
488     /**
489      * The file encoding to use when reading the source files. If the property <code>project.build.sourceEncoding</code>
490      * is not set, the platform default encoding is used. <strong>Note:</strong> This parameter always overrides the
491      * property <code>charset</code> from Checkstyle's <code>TreeWalker</code> module.
492      *
493      * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
494      * @since 2.2
495      */
496     private String encoding;
497 
498     /**
499      * @component
500      * @required
501      * @readonly
502      */
503     private SiteRenderer siteRenderer;
504 
505     /**
506      * Velocity Component.
507      */
508     private VelocityComponent velocityComponent;
509 
510     /**
511      * ServiceLocator used to lookup VelocityComponent
512      * Fix for MCHECKSTYLE-101 to avoid VelocityComponent beeing initialized when skip=true
513      */
514     private ServiceLocator serviceLocator;
515 
516     /**
517      * ${inheritDoc}
518      *
519      * @see org.codehaus.plexus.personality.plexus.lifecycle.phase.Serviceable#service(org.codehaus.plexus.personality.plexus.lifecycle.phase.ServiceLocator)
520      */
521     public void service( ServiceLocator locator )
522     {
523         this.serviceLocator = locator;
524     }
525 
526     private static final File[] EMPTY_FILE_ARRAY = new File[0];
527 
528     private StringOutputStream stringOutputStream;
529 
530     /**
531      * @component
532      * @required
533      * @readonly
534      */
535     private ResourceManager locator;
536 
537     /**
538      * @see org.apache.maven.reporting.MavenReport#getName(java.util.Locale)
539      */
540     public String getName( Locale locale )
541     {
542         return getBundle( locale ).getString( "report.checkstyle.name" );
543     }
544 
545     /**
546      * @see org.apache.maven.reporting.MavenReport#getDescription(java.util.Locale)
547      */
548     public String getDescription( Locale locale )
549     {
550         return getBundle( locale ).getString( "report.checkstyle.description" );
551     }
552 
553     /**
554      * @see org.apache.maven.reporting.AbstractMavenReport#getOutputDirectory()
555      */
556     protected String getOutputDirectory()
557     {
558         return outputDirectory.getAbsolutePath();
559     }
560 
561     /**
562      * @see org.apache.maven.reporting.AbstractMavenReport#getProject()
563      */
564     protected MavenProject getProject()
565     {
566         return project;
567     }
568 
569     /**
570      * @see org.apache.maven.reporting.AbstractMavenReport#getSiteRenderer()
571      */
572     protected SiteRenderer getSiteRenderer()
573     {
574         return siteRenderer;
575     }
576 
577     /**
578      * @see org.apache.maven.reporting.AbstractMavenReport#executeReport(java.util.Locale)
579      */
580     public void executeReport( Locale locale )
581         throws MavenReportException
582     {
583         if ( !skip )
584         {
585             mergeDeprecatedInfo();
586 
587             locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
588             locator.addSearchPath( "url", "" );
589 
590             locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
591 
592             if ( !canGenerateReport() )
593             {
594                 getLog().info( "Source directory does not exist - skipping report." );
595                 return;
596             }
597 
598             // for when we start using maven-shared-io and
599             // maven-shared-monitor...
600             // locator = new Locator( new MojoLogMonitorAdaptor( getLog() ) );
601 
602             // locator = new Locator( getLog(), new File(
603             // project.getBuild().getDirectory() ) );
604 
605             ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
606 
607             try
608             {
609                 // checkstyle will always use the context classloader in order
610                 // to load resources (dtds),
611                 // so we have to fix it
612                 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
613                 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
614 
615 
616                 String configFile = getConfigFile();
617                 Properties overridingProperties = getOverridingProperties();
618                 ModuleFactory moduleFactory;
619                 Configuration config;
620                 CheckstyleResults results;
621 
622                 moduleFactory = getModuleFactory();
623 
624                 config = ConfigurationLoader.loadConfiguration( configFile,
625                                                                 new PropertiesExpander( overridingProperties ) );
626                 String effectiveEncoding =
627                     StringUtils.isNotEmpty( encoding ) ? encoding : System.getProperty( "file.encoding", "UTF-8" );
628                 if ( StringUtils.isEmpty( encoding ) )
629                 {
630                     getLog().warn(
631                                    "File encoding has not been set, using platform encoding " + effectiveEncoding
632                                        + ", i.e. build is platform dependent!" );
633                 }
634                 Configuration[] modules = config.getChildren();
635                 for ( int i = 0; i < modules.length; i++ )
636                 {
637                     Configuration module = modules[i];
638                     if ( "TreeWalker".equals( module.getName() )
639                         || "com.puppycrawl.tools.checkstyle.TreeWalker".equals( module.getName() ) )
640                     {
641                         if ( module instanceof DefaultConfiguration )
642                         {
643                             ( (DefaultConfiguration) module ).addAttribute( "charset", effectiveEncoding );
644                         }
645                         else
646                         {
647                             getLog().warn( "Failed to configure file encoding on module " + module );
648                         }
649                     }
650                 }
651 
652                 results = executeCheckstyle( config, moduleFactory );
653 
654                 ResourceBundle bundle = getBundle( locale );
655                 generateReportStatics();
656                 generateMainReport( results, config, moduleFactory, bundle );
657                 if ( enableRSS )
658                 {
659                     generateRSS( results );
660                 }
661 
662             }
663             catch ( CheckstyleException e )
664             {
665                 throw new MavenReportException( "Failed during checkstyle configuration", e );
666             }
667             finally
668             {
669                 //be sure to restore original context classloader
670                 Thread.currentThread().setContextClassLoader( currentClassLoader );
671             }
672         }
673     }
674 
675     private void generateReportStatics()
676         throws MavenReportException
677     {
678         ReportResource rresource = new ReportResource( PLUGIN_RESOURCES, outputDirectory );
679         try
680         {
681             rresource.copy( "images/rss.png" );
682         }
683         catch ( IOException e )
684         {
685             throw new MavenReportException( "Unable to copy static resources.", e );
686         }
687     }
688 
689     private void generateRSS( CheckstyleResults results )
690         throws MavenReportException
691     {
692         if ( velocityComponent == null )
693         {
694             try
695             {
696                 velocityComponent = (VelocityComponent) serviceLocator.lookup( VelocityComponent.ROLE );
697             }
698             catch ( ComponentLookupException e )
699             {
700                 throw new MavenReportException( "Failed to setup Velocity", e );
701             }
702         }
703 
704         VelocityTemplate vtemplate = new VelocityTemplate( velocityComponent, PLUGIN_RESOURCES );
705         vtemplate.setLog( getLog() );
706 
707         Context context = new VelocityContext();
708         context.put( "results", results );
709         context.put( "project", project );
710         context.put( "copyright", getCopyright() );
711         context.put( "levelInfo", SeverityLevel.INFO );
712         context.put( "levelWarning", SeverityLevel.WARNING );
713         context.put( "levelError", SeverityLevel.ERROR );
714         context.put( "stringutils", new StringUtils() );
715 
716         try
717         {
718             vtemplate.generate( outputDirectory.getPath() + "/checkstyle.rss", "checkstyle-rss.vm", context );
719         }
720         catch ( ResourceNotFoundException e )
721         {
722             throw new MavenReportException( "Unable to find checkstyle-rss.vm resource.", e );
723         }
724         catch ( MojoExecutionException e )
725         {
726             throw new MavenReportException( "Unable to generate checkstyle.rss.", e );
727         }
728         catch ( VelocityException e )
729         {
730             throw new MavenReportException( "Unable to generate checkstyle.rss.", e );
731         }
732         catch ( IOException e )
733         {
734             throw new MavenReportException( "Unable to generate checkstyle.rss.", e );
735         }
736     }
737 
738     private String getCopyright()
739     {
740         String copyright;
741         int currentYear = Calendar.getInstance().get( Calendar.YEAR );
742         if ( StringUtils.isNotEmpty( project.getInceptionYear() )
743             && !String.valueOf( currentYear ).equals( project.getInceptionYear() ) )
744         {
745             copyright = project.getInceptionYear() + " - " + currentYear;
746         }
747         else
748         {
749             copyright = String.valueOf( currentYear );
750         }
751 
752         if ( ( project.getOrganization() != null ) && StringUtils.isNotEmpty( project.getOrganization().getName() ) )
753         {
754             copyright = copyright + " " + project.getOrganization().getName();
755         }
756         return copyright;
757     }
758 
759     private void generateMainReport( CheckstyleResults results, Configuration config, ModuleFactory moduleFactory,
760                                      ResourceBundle bundle )
761     {
762         CheckstyleReportGenerator generator = new CheckstyleReportGenerator( getSink(), bundle, project.getBasedir(), siteTool );
763 
764         generator.setLog( getLog() );
765         generator.setEnableRulesSummary( enableRulesSummary );
766         generator.setEnableSeveritySummary( enableSeveritySummary );
767         generator.setEnableFilesSummary( enableFilesSummary );
768         generator.setEnableRSS( enableRSS );
769         generator.setCheckstyleConfig( config );
770         generator.setCheckstyleModuleFactory( moduleFactory );
771         if ( linkXRef )
772         {
773             String relativePath = PathTool.getRelativePath( getOutputDirectory(), xrefLocation.getAbsolutePath() );
774             if ( StringUtils.isEmpty( relativePath ) )
775             {
776                 relativePath = ".";
777             }
778             relativePath = relativePath + "/" + xrefLocation.getName();
779             if ( xrefLocation.exists() )
780             {
781                 // XRef was already generated by manual execution of a lifecycle
782                 // binding
783                 generator.setXrefLocation( relativePath );
784             }
785             else
786             {
787                 // Not yet generated - check if the report is on its way
788                 for ( Iterator reports = getProject().getReportPlugins().iterator(); reports.hasNext(); )
789                 {
790                     ReportPlugin report = (ReportPlugin) reports.next();
791 
792                     String artifactId = report.getArtifactId();
793                     if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
794                     {
795                         generator.setXrefLocation( relativePath );
796                     }
797                 }
798             }
799 
800             if ( generator.getXrefLocation() == null )
801             {
802                 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
803             }
804         }
805         generator.generateReport( results );
806     }
807 
808     /**
809      * Merge in the deprecated parameters to the new ones, unless the new
810      * parameters have values.
811      *
812      * @deprecated Remove when deprecated params are removed.
813      */
814     private void mergeDeprecatedInfo()
815     {
816         if ( "config/sun_checks.xml".equals( configLocation ) && !"sun".equals( format ) )
817         {
818             configLocation = (String) FORMAT_TO_CONFIG_LOCATION.get( format );
819         }
820 
821         if ( StringUtils.isEmpty( propertiesLocation ) )
822         {
823             if ( propertiesFile != null )
824             {
825                 propertiesLocation = propertiesFile.getPath();
826             }
827             else if ( propertiesURL != null )
828             {
829                 propertiesLocation = propertiesURL.toExternalForm();
830             }
831         }
832 
833         if ( "LICENSE.txt".equals( headerLocation ) )
834         {
835             File defaultHeaderFile = new File( project.getBasedir(), "LICENSE.txt" );
836             if ( !defaultHeaderFile.equals( headerFile ) )
837             {
838                 headerLocation = headerFile.getPath();
839             }
840         }
841 
842         if ( StringUtils.isEmpty( suppressionsLocation ) )
843         {
844             suppressionsLocation = suppressionsFile;
845         }
846 
847         if ( StringUtils.isEmpty( packageNamesLocation ) )
848         {
849             packageNamesLocation = packageNamesFile;
850         }
851     }
852 
853     private CheckstyleResults executeCheckstyle( Configuration config, ModuleFactory moduleFactory )
854         throws MavenReportException, CheckstyleException
855     {
856         File[] files;
857         try
858         {
859             files = getFilesToProcess( includes, excludes );
860         }
861         catch ( IOException e )
862         {
863             throw new MavenReportException( "Error getting files to process", e );
864         }
865 
866         FilterSet filterSet = getSuppressions();
867 
868         Checker checker = new Checker();
869 
870         // setup classloader, needed to avoid "Unable to get class information
871         // for ..." errors
872         List classPathStrings;
873         List outputDirectories = new ArrayList();
874         try
875         {
876             classPathStrings = this.project.getCompileClasspathElements();
877             outputDirectories.add( this.project.getBuild().getOutputDirectory() );
878 
879             if ( includeTestSourceDirectory && ( testSourceDirectory != null ) && ( testSourceDirectory.exists() )
880                 && ( testSourceDirectory.isDirectory() ) )
881             {
882                 classPathStrings = this.project.getTestClasspathElements();
883                 outputDirectories.add( this.project.getBuild().getTestOutputDirectory() );
884             }
885         }
886         catch ( DependencyResolutionRequiredException e )
887         {
888             throw new MavenReportException( e.getMessage(), e );
889         }
890 
891         List urls = new ArrayList( classPathStrings.size() );
892 
893         Iterator iter = classPathStrings.iterator();
894         while ( iter.hasNext() )
895         {
896             try
897             {
898                 urls.add( new File( ( (String) iter.next() ) ).toURL() );
899             }
900             catch ( MalformedURLException e )
901             {
902                 throw new MavenReportException( e.getMessage(), e );
903             }
904         }
905 
906         Iterator iterator = outputDirectories.iterator();
907         while ( iterator.hasNext() )
908         {
909             try
910             {
911                 String outputDirectoryString = (String) iterator.next();
912                 if ( outputDirectoryString != null )
913                 {
914                     File outputDirectoryFile = new File( outputDirectoryString );
915                     if ( outputDirectoryFile.exists() )
916                     {
917                         URL outputDirectoryUrl = outputDirectoryFile.toURL();
918                         getLog().debug( "Adding the outputDirectory " + outputDirectoryUrl.toString()
919                             + " to the Checkstyle class path" );
920                         urls.add( outputDirectoryUrl );
921                     }
922                 }
923             }
924             catch ( MalformedURLException e )
925             {
926                 throw new MavenReportException( e.getMessage(), e );
927             }
928             catch ( IOException e )
929             {
930                 throw new MavenReportException( e.getMessage(), e );
931             }
932         }
933 
934         URLClassLoader projectClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[urls.size()] ), null );
935         checker.setClassloader( projectClassLoader );
936 
937         if ( moduleFactory != null )
938         {
939             checker.setModuleFactory( moduleFactory );
940         }
941 
942         if ( filterSet != null )
943         {
944             checker.addFilter( filterSet );
945         }
946 
947         checker.configure( config );
948 
949         AuditListener listener = getListener();
950 
951         if ( listener != null )
952         {
953             checker.addListener( listener );
954         }
955 
956         if ( consoleOutput )
957         {
958             checker.addListener( getConsoleListener() );
959         }
960 
961         CheckstyleReportListener sinkListener = new CheckstyleReportListener( sourceDirectory );
962         if ( includeTestSourceDirectory && ( testSourceDirectory != null ) && ( testSourceDirectory.exists() )
963             && ( testSourceDirectory.isDirectory() ) )
964         {
965             sinkListener.addSourceDirectory( testSourceDirectory );
966         }
967 
968         checker.addListener( sinkListener );
969 
970         int nbErrors = checker.process( files );
971 
972         checker.destroy();
973 
974         if ( stringOutputStream != null )
975         {
976             getLog().info( stringOutputStream.toString() );
977         }
978 
979         if ( failsOnError && nbErrors > 0 )
980         {
981             // TODO: should be a failure, not an error. Report is not meant to
982             // throw an exception here (so site would
983             // work regardless of config), but should record this information
984             throw new MavenReportException( "There are " + nbErrors + " checkstyle errors." );
985         }
986         else if ( nbErrors > 0 )
987         {
988             getLog().info( "There are " + nbErrors + " checkstyle errors." );
989         }
990 
991         return sinkListener.getResults();
992     }
993 
994     /**
995      * @see org.apache.maven.reporting.MavenReport#getOutputName()
996      */
997     public String getOutputName()
998     {
999         return "checkstyle";
1000     }
1001 
1002     private AuditListener getListener()
1003         throws MavenReportException
1004     {
1005         AuditListener listener = null;
1006 
1007         if ( StringUtils.isNotEmpty( outputFileFormat ) )
1008         {
1009             File resultFile = outputFile;
1010 
1011             OutputStream out = getOutputStream( resultFile );
1012 
1013             if ( "xml".equals( outputFileFormat ) )
1014             {
1015                 listener = new XMLLogger( out, true );
1016             }
1017             else if ( "plain".equals( outputFileFormat ) )
1018             {
1019                 listener = new DefaultLogger( out, true );
1020             }
1021             else
1022             {
1023                 // TODO: failure if not a report
1024                 throw new MavenReportException( "Invalid output file format: (" + outputFileFormat
1025                     + "). Must be 'plain' or 'xml'." );
1026             }
1027         }
1028 
1029         return listener;
1030     }
1031 
1032     private OutputStream getOutputStream( File file )
1033         throws MavenReportException
1034     {
1035         File parentFile = file.getAbsoluteFile().getParentFile();
1036 
1037         if ( !parentFile.exists() )
1038         {
1039             parentFile.mkdirs();
1040         }
1041 
1042         FileOutputStream fileOutputStream;
1043         try
1044         {
1045             fileOutputStream = new FileOutputStream( file );
1046         }
1047         catch ( FileNotFoundException e )
1048         {
1049             throw new MavenReportException( "Unable to create output stream: " + file, e );
1050         }
1051         return fileOutputStream;
1052     }
1053 
1054     private File[] getFilesToProcess( String includes, String excludes )
1055         throws IOException
1056     {
1057         StringBuffer excludesStr = new StringBuffer();
1058 
1059         if ( StringUtils.isNotEmpty( excludes ) )
1060         {
1061             excludesStr.append( excludes );
1062         }
1063 
1064         String[] defaultExcludes = FileUtils.getDefaultExcludes();
1065         for ( int i = 0; i < defaultExcludes.length; i++ )
1066         {
1067             if ( excludesStr.length() > 0 )
1068             {
1069                 excludesStr.append( "," );
1070             }
1071 
1072             excludesStr.append( defaultExcludes[i] );
1073         }
1074 
1075         List files = FileUtils.getFiles( sourceDirectory, includes, excludesStr.toString() );
1076         if ( includeTestSourceDirectory && ( testSourceDirectory != null ) && ( testSourceDirectory.exists() )
1077             && ( testSourceDirectory.isDirectory() ) )
1078         {
1079             files.addAll( FileUtils.getFiles( testSourceDirectory, includes, excludesStr.toString() ) );
1080         }
1081 
1082         return (File[]) files.toArray( EMPTY_FILE_ARRAY );
1083     }
1084 
1085     private Properties getOverridingProperties()
1086         throws MavenReportException
1087     {
1088         Properties p = new Properties();
1089 
1090         try
1091         {
1092             File propertiesFile = locator.resolveLocation( propertiesLocation, "checkstyle-checker.properties" );
1093 
1094             if ( propertiesFile != null )
1095             {
1096                 p.load( new FileInputStream( propertiesFile ) );
1097             }
1098 
1099             if ( StringUtils.isNotEmpty( propertyExpansion ) )
1100             {
1101                 // Convert \ to \\, so that p.load will convert it back properly
1102                 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
1103                 p.load( new StringInputStream( propertyExpansion ) );
1104             }
1105 
1106             // Workaround for MCHECKSTYLE-48
1107             // Make sure that "config/maven-header.txt" is the default value
1108             // for headerLocation, if configLocation="config/maven_checks.xml"
1109             if ( "config/maven_checks.xml".equals( configLocation ) )
1110             {
1111                 if ( "LICENSE.txt".equals( headerLocation ) )
1112                 {
1113                     headerLocation = "config/maven-header.txt";
1114                 }
1115             }
1116             if ( StringUtils.isNotEmpty( headerLocation ) )
1117             {
1118                 try
1119                 {
1120                     File headerFile = locator.resolveLocation( headerLocation, "checkstyle-header.txt" );
1121 
1122                     if ( headerFile != null )
1123                     {
1124                         p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
1125                     }
1126                 }
1127                 catch ( IOException e )
1128                 {
1129                     throw new MavenReportException( "Unable to process header location: " + headerLocation, e );
1130                 }
1131             }
1132 
1133             if ( cacheFile != null )
1134             {
1135                 p.setProperty( "checkstyle.cache.file", cacheFile );
1136             }
1137         }
1138         catch ( IOException e )
1139         {
1140             throw new MavenReportException( "Failed to get overriding properties", e );
1141         }
1142 
1143         if ( suppressionsFileExpression != null )
1144         {
1145             String suppresionFile = getSuppressionLocation();
1146 
1147             if ( suppresionFile != null )
1148             {
1149                 p.setProperty( suppressionsFileExpression, suppresionFile );
1150             }
1151         }
1152 
1153         return p;
1154     }
1155 
1156     private String getConfigFile()
1157         throws MavenReportException
1158     {
1159         try
1160         {
1161             File configFile = locator.getResourceAsFile( configLocation, "checkstyle-checker.xml" );
1162 
1163             if ( configFile == null )
1164             {
1165                 throw new MavenReportException( "Unable to process config location: " + configLocation );
1166             }
1167             return configFile.getAbsolutePath();
1168         }
1169         catch ( org.codehaus.plexus.resource.loader.ResourceNotFoundException e )
1170         {
1171             throw new MavenReportException( "Unable to find configuration file at location "
1172                                             + configLocation, e );
1173         }
1174         catch ( FileResourceCreationException e )
1175         {
1176             throw new MavenReportException( "Unable to process configuration file location "
1177                                             + configLocation, e );
1178         }
1179 
1180     }
1181 
1182     private ModuleFactory getModuleFactory()
1183         throws CheckstyleException
1184     {
1185         // default to internal module factory.
1186         ModuleFactory moduleFactory = PackageNamesLoader.loadModuleFactory( Thread.currentThread()
1187             .getContextClassLoader() );
1188 
1189         try
1190         {
1191             // attempt to locate any specified package file.
1192             File packageNamesFile = locator.resolveLocation( packageNamesLocation, "checkstyle-packages.xml" );
1193 
1194             if ( packageNamesFile != null )
1195             {
1196                 // load resolved location.
1197                 moduleFactory = PackageNamesLoader.loadModuleFactory( packageNamesFile.getAbsolutePath() );
1198             }
1199         }
1200         catch ( IOException e )
1201         {
1202             getLog().error( "Unable to process package names location: " + packageNamesLocation, e );
1203         }
1204         return moduleFactory;
1205     }
1206 
1207     private String getSuppressionLocation()
1208         throws MavenReportException
1209     {
1210         try
1211         {
1212             File suppressionsFile = locator.resolveLocation( suppressionsLocation, "checkstyle-suppressions.xml" );
1213 
1214             if ( suppressionsFile == null )
1215             {
1216                 return null;
1217             }
1218 
1219             return suppressionsFile.getAbsolutePath();
1220         }
1221         catch ( IOException e )
1222         {
1223             throw new MavenReportException( "Failed to process supressions location: " + suppressionsLocation, e );
1224         }
1225     }
1226 
1227     private FilterSet getSuppressions()
1228         throws MavenReportException
1229     {
1230         try
1231         {
1232             File suppressionsFile = locator.resolveLocation( suppressionsLocation, "checkstyle-suppressions.xml" );
1233 
1234             if ( suppressionsFile == null )
1235             {
1236                 return null;
1237             }
1238 
1239             return SuppressionsLoader.loadSuppressions( suppressionsFile.getAbsolutePath() );
1240         }
1241         catch ( CheckstyleException ce )
1242         {
1243             throw new MavenReportException( "failed to load suppressions location: " + suppressionsLocation, ce );
1244         }
1245         catch ( IOException e )
1246         {
1247             throw new MavenReportException( "Failed to process supressions location: " + suppressionsLocation, e );
1248         }
1249     }
1250 
1251     private DefaultLogger getConsoleListener()
1252         throws MavenReportException
1253     {
1254         DefaultLogger consoleListener;
1255 
1256         if ( useFile == null )
1257         {
1258             stringOutputStream = new StringOutputStream();
1259             consoleListener = new DefaultLogger( stringOutputStream, false );
1260         }
1261         else
1262         {
1263             OutputStream out = getOutputStream( useFile );
1264 
1265             consoleListener = new DefaultLogger( out, true );
1266         }
1267 
1268         return consoleListener;
1269     }
1270 
1271     private static ResourceBundle getBundle( Locale locale )
1272     {
1273         return ResourceBundle.getBundle( "checkstyle-report", locale, CheckstyleReport.class.getClassLoader() );
1274     }
1275 
1276     /**
1277      * @see org.apache.maven.reporting.AbstractMavenReport#canGenerateReport()
1278      */
1279     public boolean canGenerateReport()
1280     {
1281         // TODO: would be good to scan the files here
1282         return sourceDirectory.exists();
1283     }
1284 
1285     /**
1286      * @see org.apache.maven.reporting.AbstractMavenReport#setReportOutputDirectory(java.io.File)
1287      */
1288     public void setReportOutputDirectory( File reportOutputDirectory )
1289     {
1290         super.setReportOutputDirectory( reportOutputDirectory );
1291         this.outputDirectory = reportOutputDirectory;
1292     }
1293 }