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 java.io.BufferedReader;
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileNotFoundException;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.OutputStream;
29  import java.io.Reader;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.apache.maven.artifact.Artifact;
36  import org.apache.maven.model.Dependency;
37  import org.apache.maven.model.Plugin;
38  import org.apache.maven.model.PluginManagement;
39  import org.apache.maven.model.Resource;
40  import org.apache.maven.plugin.AbstractMojo;
41  import org.apache.maven.plugin.MojoExecution;
42  import org.apache.maven.plugin.MojoExecutionException;
43  import org.apache.maven.plugin.MojoFailureException;
44  import org.apache.maven.plugin.checkstyle.exec.CheckstyleExecutor;
45  import org.apache.maven.plugin.checkstyle.exec.CheckstyleExecutorException;
46  import org.apache.maven.plugin.checkstyle.exec.CheckstyleExecutorRequest;
47  import org.apache.maven.plugin.descriptor.PluginDescriptor;
48  import org.apache.maven.plugins.annotations.Component;
49  import org.apache.maven.plugins.annotations.LifecyclePhase;
50  import org.apache.maven.plugins.annotations.Mojo;
51  import org.apache.maven.plugins.annotations.Parameter;
52  import org.apache.maven.plugins.annotations.ResolutionScope;
53  import org.apache.maven.project.MavenProject;
54  import org.codehaus.plexus.configuration.PlexusConfiguration;
55  import org.codehaus.plexus.util.FileUtils;
56  import org.codehaus.plexus.util.IOUtil;
57  import org.codehaus.plexus.util.ReaderFactory;
58  import org.codehaus.plexus.util.StringUtils;
59  import org.codehaus.plexus.util.xml.pull.MXParser;
60  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
61  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
62  
63  import com.puppycrawl.tools.checkstyle.DefaultLogger;
64  import com.puppycrawl.tools.checkstyle.XMLLogger;
65  import com.puppycrawl.tools.checkstyle.api.AuditListener;
66  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
67  
68  /**
69   * Performs Checkstyle analysis and outputs violations or a count of violations
70   * to the console, potentially failing the build.
71   * It can also be configured to re-use an earlier analysis.
72   *
73   * @author <a href="mailto:joakim@erdfelt.net">Joakim Erdfelt</a>
74   * @version $Id: CheckstyleViolationCheckMojo.java 1621538 2014-08-30 21:12:11Z hboutemy $
75   */
76  @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.TEST,
77         threadSafe = true )
78  public class CheckstyleViolationCheckMojo
79      extends AbstractMojo
80  {
81  
82      private static final String JAVA_FILES = "**\\/*.java";
83  
84      private static final String CHECKSTYLE_FILE_HEADER = "<?xml version=\"1.0\"?>\n"
85              + "<!DOCTYPE module PUBLIC \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n"
86              + "        \"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">\n";
87  
88      /**
89       * Specifies the path and filename to save the Checkstyle output. The format
90       * of the output file is determined by the <code>outputFileFormat</code>
91       * parameter.
92       */
93      @Parameter( property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml" )
94      private File outputFile;
95  
96      /**
97       * Specifies the format of the output to be used when writing to the output
98       * file. Valid values are "<code>plain</code>" and "<code>xml</code>".
99       */
100     @Parameter( property = "checkstyle.output.format", defaultValue = "xml" )
101     private String outputFileFormat;
102 
103     /**
104      * Fail the build on a violation. The goal checks for the violations
105      * after logging them (if {@link #logViolationsToConsole} is 'true').
106      * Compare this to {@link #failsOnError} which fails the build immediately
107      * before examining the output log.
108      */
109     @Parameter( property = "checkstyle.failOnViolation", defaultValue = "true" )
110     private boolean failOnViolation;
111 
112     /**
113      * The maximum number of allowed violations. The execution fails only if the
114      * number of violations is above this limit.
115      *
116      * @since 2.3
117      */
118     @Parameter( property = "checkstyle.maxAllowedViolations", defaultValue = "0" )
119     private int maxAllowedViolations;
120 
121     /**
122      * The lowest severity level that is considered a violation.
123      * Valid values are "<code>error</code>", "<code>warning</code>" and "<code>info</code>".
124      *
125      * @since 2.2
126      */
127     @Parameter( property = "checkstyle.violationSeverity", defaultValue = "error" )
128     private String violationSeverity = "error";
129 
130     /**
131      * Violations to ignore. This is a comma-separated list, each value being either
132      * a rule name, a rule category or a java package name of rule class.
133      *
134      * @since 2.13
135      */
136     @Parameter( property = "checkstyle.violation.ignore" )
137     private String violationIgnore;
138 
139     /**
140      * Skip entire check.
141      *
142      * @since 2.2
143      */
144     @Parameter( property = "checkstyle.skip", defaultValue = "false" )
145     private boolean skip;
146 
147     /**
148      * Skip Checkstyle execution will only scan the outputFile.
149      *
150      * @since 2.5
151      */
152     @Parameter( property = "checkstyle.skipExec", defaultValue = "false" )
153     private boolean skipExec;
154 
155     /**
156      * Output the detected violations to the console.
157      *
158      * @since 2.3
159      */
160     @Parameter( property = "checkstyle.console", defaultValue = "true" )
161     private boolean logViolationsToConsole;
162 
163     /**
164      * Specifies the location of the resources to be used for Checkstyle.
165      *
166      * @since 2.11
167      */
168     @Parameter( defaultValue = "${project.resources}", readonly = true )
169     protected List<Resource> resources;
170 
171     /**
172      * <p>
173      * Specifies the location of the XML configuration to use.
174      * </p>
175      * <p/>
176      * <p>
177      * Potential values are a filesystem path, a URL, or a classpath resource.
178      * This parameter expects that the contents of the location conform to the
179      * xml format (Checkstyle <a
180      * href="http://checkstyle.sourceforge.net/config.html#Modules">Checker
181      * module</a>) configuration of rulesets.
182      * </p>
183      * <p/>
184      * <p>
185      * This parameter is resolved as resource, URL, then file. If successfully
186      * resolved, the contents of the configuration is copied into the
187      * <code>${project.build.directory}/checkstyle-configuration.xml</code>
188      * file before being passed to Checkstyle as a configuration.
189      * </p>
190      * <p/>
191      * <p>
192      * There are 4 predefined rulesets.
193      * </p>
194      * <p/>
195      * <ul>
196      * <li><code>config/sun_checks.xml</code>: Sun Checks.</li>
197      * <li><code>config/turbine_checks.xml</code>: Turbine Checks.</li>
198      * <li><code>config/avalon_checks.xml</code>: Avalon Checks.</li>
199      * <li><code>config/maven_checks.xml</code>: Maven Source Checks.</li>
200      * </ul>
201      *
202      * @since 2.5
203      */
204     @Parameter( property = "checkstyle.config.location", defaultValue = "config/sun_checks.xml" )
205     private String configLocation;
206 
207     /**
208      * <p>
209      * Specifies the location of the properties file.
210      * </p>
211      * <p/>
212      * <p>
213      * This parameter is resolved as URL, File then resource. If successfully
214      * resolved, the contents of the properties location is copied into the
215      * <code>${project.build.directory}/checkstyle-checker.properties</code>
216      * file before being passed to Checkstyle for loading.
217      * </p>
218      * <p/>
219      * <p>
220      * The contents of the <code>propertiesLocation</code> will be made
221      * available to Checkstyle for specifying values for parameters within the
222      * xml configuration (specified in the <code>configLocation</code>
223      * parameter).
224      * </p>
225      *
226      * @since 2.5
227      */
228     @Parameter( property = "checkstyle.properties.location" )
229     private String propertiesLocation;
230 
231     /**
232      * Allows for specifying raw property expansion information.
233      */
234     @Parameter
235     private String propertyExpansion;
236 
237     /**
238      * <p>
239      * Specifies the location of the License file (a.k.a. the header file) that
240      * can be used by Checkstyle to verify that source code has the correct
241      * license header.
242      * </p>
243      * <p>
244      * You need to use ${checkstyle.header.file} in your Checkstyle xml
245      * configuration to reference the name of this header file.
246      * </p>
247      * <p>
248      * For instance:
249      * </p>
250      * <p>
251      * <code>
252      * &lt;module name="RegexpHeader">
253      * &lt;property name="headerFile" value="${checkstyle.header.file}"/>
254      * &lt;/module>
255      * </code>
256      * </p>
257      *
258      * @since 2.0-beta-2
259      */
260     @Parameter( property = "checkstyle.header.file", defaultValue = "LICENSE.txt" )
261     private String headerLocation;
262 
263     /**
264      * Specifies the cache file used to speed up Checkstyle on successive runs.
265      */
266     @Parameter( defaultValue = "${project.build.directory}/checkstyle-cachefile" )
267     private String cacheFile;
268 
269     /**
270      * The key to be used in the properties for the suppressions file.
271      *
272      * @since 2.1
273      */
274     @Parameter( property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file" )
275     private String suppressionsFileExpression;
276 
277     /**
278      * <p>
279      * Specifies the location of the suppressions XML file to use.
280      * </p>
281      * <p/>
282      * <p>
283      * This parameter is resolved as resource, URL, then file. If successfully
284      * resolved, the contents of the suppressions XML is copied into the
285      * <code>${project.build.directory}/checkstyle-suppressions.xml</code> file
286      * before being passed to Checkstyle for loading.
287      * </p>
288      * <p/>
289      * <p>
290      * See <code>suppressionsFileExpression</code> for the property that will
291      * be made available to your Checkstyle configuration.
292      * </p>
293      *
294      * @since 2.0-beta-2
295      */
296     @Parameter( property = "checkstyle.suppressions.location" )
297     private String suppressionsLocation;
298 
299     /**
300      * The file encoding to use when reading the source files. If the property <code>project.build.sourceEncoding</code>
301      * is not set, the platform default encoding is used. <strong>Note:</strong> This parameter always overrides the
302      * property <code>charset</code> from Checkstyle's <code>TreeWalker</code> module.
303      *
304      * @since 2.2
305      */
306     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
307     private String encoding;
308 
309     /**
310      * @since 2.5
311      */
312     @Component( role = CheckstyleExecutor.class, hint = "default" )
313     protected CheckstyleExecutor checkstyleExecutor;
314 
315     /**
316      * Output errors to console.
317      */
318     @Parameter( property = "checkstyle.consoleOutput", defaultValue = "false" )
319     private boolean consoleOutput;
320 
321     /**
322      * The Maven Project Object.
323      */
324     @Parameter ( defaultValue = "${project}", readonly = true, required = true )
325     protected MavenProject project;
326     
327     /**
328      * The Plugin Descriptor
329      */
330     @Parameter( defaultValue = "${plugin}", readonly = true, required = true )
331     private PluginDescriptor plugin;
332 
333     // remove when requiring Maven 3.x, just use #plugin 
334     @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true )
335     private MojoExecution mojoExecution;
336 
337     /**
338      * If <code>null</code>, the Checkstyle plugin will display violations on stdout.
339      * Otherwise, a text file will be created with the violations.
340      */
341     @Parameter
342     private File useFile;
343 
344     /**
345      * Specifies the names filter of the source files to be excluded for
346      * Checkstyle.
347      */
348     @Parameter( property = "checkstyle.excludes" )
349     private String excludes;
350 
351     /**
352      * Specifies the names filter of the source files to be used for Checkstyle.
353      */
354     @Parameter( property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true )
355     private String includes;
356 
357     /**
358      * Specifies the names filter of the files to be excluded for
359      * Checkstyle when checking resources.
360      * @since 2.11
361      */
362     @Parameter( property = "checkstyle.resourceExcludes" )
363     private String resourceExcludes;
364 
365     /**
366      * Specifies the names filter of the files to be used for Checkstyle when checking resources.
367      * @since 2.11
368      */
369     @Parameter( property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true )
370     private String resourceIncludes;
371 
372     /**
373      * If this is true, and Checkstyle reported any violations or errors,
374      * the build fails immediately after running Checkstyle, before checking the log
375      * for {@link #logViolationsToConsole}. If you want to use {@link #logViolationsToConsole},
376      * use {@link #failOnViolation} instead of this.
377      */
378     @Parameter( defaultValue = "false" )
379     private boolean failsOnError;
380 
381     /**
382      * Specifies the location of the test source directory to be used for
383      * Checkstyle.
384      *
385      * @since 2.2
386      * @deprecated instead use {@link #testSourceDirectories} 
387      */
388     @Deprecated
389     @Parameter
390     private File testSourceDirectory;
391 
392     /**
393      * Specifies the location of the test source directories to be used for
394      * Checkstyle.
395      * @since 2.13
396      */
397     @Parameter( defaultValue = "${project.testCompileSourceRoots}" )
398     private List<String> testSourceDirectories;
399 
400     /**
401      * Include or not the test source directory to be used for Checkstyle.
402      *
403      * @since 2.2
404      */
405     @Parameter( defaultValue = "false" )
406     private boolean includeTestSourceDirectory;
407 
408     /**
409      * Specifies the location of the source directory to be used for Checkstyle.
410      * 
411      * @deprecated instead use {@link #sourceDirectories}
412      */
413     @Deprecated
414     @Parameter
415     private File sourceDirectory;
416 
417     /**
418      * Specifies the location of the source directories to be used for Checkstyle.
419      * @since 2.13
420      */
421     @Parameter( defaultValue = "${project.compileSourceRoots}" )
422     private List<String> sourceDirectories;
423 
424     /**
425      * Whether to apply Checkstyle to resource directories.
426      * @since 2.11
427      */
428     @Parameter( property = "checkstyle.includeResources", defaultValue = "true", required = true )
429     private boolean includeResources = true;
430 
431     /**
432      * Whether to apply Checkstyle to test resource directories.
433      * @since 2.11
434      */
435     @Parameter( property = "checkstyle.includeTestResources", defaultValue = "true", required = true )
436     private boolean includeTestResources = true;
437 
438     /**
439      * By using this property, you can specify the whole Checkstyle rules
440      * inline directly inside this pom.
441      *
442      * <pre>
443      * &lt;plugin&gt;
444      *   ...
445      *   &lt;configuration&gt;
446      *     &lt;checkstyleRules&gt;
447      *       &lt;module name="Checker"&gt;
448      *         &lt;module name="FileTabCharacter"&gt;
449      *           &lt;property name="eachLine" value="true" /&gt;
450      *         &lt;/module&gt;
451      *         &lt;module name="TreeWalker"&gt;
452      *           &lt;module name="EmptyBlock"/&gt;
453      *         &lt;/module&gt;
454      *       &lt;/module&gt;
455      *     &lt;/checkstyleRules&gt;
456      *   &lt;/configuration&gt;
457      *   ...
458      * </pre>
459      *
460      * @since 2.12
461      */
462     @Parameter
463     private PlexusConfiguration checkstyleRules;
464 
465     /**
466      * dump file for inlined Checkstyle rules 
467      */
468     @Parameter( property = "checkstyle.output.rules.file",
469                     defaultValue = "${project.build.directory}/checkstyle-rules.xml" )
470     private File rulesFiles;
471 
472     private ByteArrayOutputStream stringOutputStream;
473 
474     /** {@inheritDoc} */
475     public void execute()
476         throws MojoExecutionException, MojoFailureException
477     {
478         if ( skip )
479         {
480             return;
481         }
482 
483         if ( !skipExec )
484         {
485             if ( checkstyleRules != null )
486             {
487                 if ( !"config/sun_checks.xml".equals( configLocation ) )
488                 {
489                     throw new MojoExecutionException( "If you use inline configuration for rules, don't specify "
490                         + "a configLocation" );
491                 }
492                 if ( checkstyleRules.getChildCount() > 1 )
493                 {
494                     throw new MojoExecutionException( "Currently only one root module is supported" );
495                 }
496 
497                 PlexusConfiguration checkerModule = checkstyleRules.getChild( 0 );
498 
499                 try
500                 {
501                     FileUtils.forceMkdir( rulesFiles.getParentFile() );
502                     FileUtils.fileWrite( rulesFiles, CHECKSTYLE_FILE_HEADER + checkerModule.toString() );
503                 }
504                 catch ( final IOException e )
505                 {
506                     throw new MojoExecutionException( e.getMessage(), e );
507                 }
508                 configLocation = rulesFiles.getAbsolutePath();
509             }
510 
511             ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
512 
513             try
514             {
515                 CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
516                 request.setConsoleListener( getConsoleListener() ).setConsoleOutput( consoleOutput )
517                     .setExcludes( excludes ).setFailsOnError( failsOnError ).setIncludes( includes )
518                     .setResourceIncludes( resourceIncludes )
519                     .setResourceExcludes( resourceExcludes )
520                     .setIncludeResources( includeResources )
521                     .setIncludeTestResources( includeTestResources )
522                     .setIncludeTestSourceDirectory( includeTestSourceDirectory ).setListener( getListener() )
523                     .setLog( getLog() ).setProject( project ).setSourceDirectories( getSourceDirectories() )
524                     .setResources( resources )
525                     .setStringOutputStream( stringOutputStream ).setSuppressionsLocation( suppressionsLocation )
526                     .setTestSourceDirectories( getTestSourceDirectories() ).setConfigLocation( configLocation )
527                     .setConfigurationArtifacts( collectArtifacts( "config" ) )
528                     .setPropertyExpansion( propertyExpansion )
529                     .setHeaderLocation( headerLocation ).setLicenseArtifacts( collectArtifacts( "license" ) )
530                     .setCacheFile( cacheFile ).setSuppressionsFileExpression( suppressionsFileExpression )
531                     .setEncoding( encoding ).setPropertiesLocation( propertiesLocation );
532                 checkstyleExecutor.executeCheckstyle( request );
533 
534             }
535             catch ( CheckstyleException e )
536             {
537                 throw new MojoExecutionException( "Failed during checkstyle configuration", e );
538             }
539             catch ( CheckstyleExecutorException e )
540             {
541                 throw new MojoExecutionException( "Failed during checkstyle execution", e );
542             }
543             finally
544             {
545                 //be sure to restore original context classloader
546                 Thread.currentThread().setContextClassLoader( currentClassLoader );
547             }
548         }
549 
550         if ( !"xml".equals( outputFileFormat ) )
551         {
552             throw new MojoExecutionException( "Output format is '" + outputFileFormat
553                 + "', checkstyle:check requires format to be 'xml'." );
554         }
555 
556         if ( !outputFile.exists() )
557         {
558             getLog().info( "Unable to perform checkstyle:check, unable to find checkstyle:checkstyle outputFile." );
559             return;
560         }
561 
562         Reader reader = null;
563         try
564         {
565             reader = new BufferedReader( ReaderFactory.newXmlReader( outputFile ) );
566 
567             XmlPullParser xpp = new MXParser();
568             xpp.setInput( reader );
569 
570             int violations = countViolations( xpp );
571 
572             if ( violations > maxAllowedViolations )
573             {
574                 if ( failOnViolation )
575                 {
576                     String msg =
577                         "You have " + violations + " Checkstyle violation" + ( ( violations > 1 ) ? "s" : "" ) + ".";
578                     if ( maxAllowedViolations > 0 )
579                     {
580                         msg += " The maximum number of allowed violations is " + maxAllowedViolations + ".";
581                     }
582                     throw new MojoFailureException( msg );
583                 }
584 
585                 getLog().warn( "checkstyle:check violations detected but failOnViolation set to false" );
586             }
587         }
588         catch ( IOException e )
589         {
590             throw new MojoExecutionException( "Unable to read Checkstyle results xml: " + outputFile.getAbsolutePath(),
591                                               e );
592         }
593         catch ( XmlPullParserException e )
594         {
595             throw new MojoExecutionException( "Unable to read Checkstyle results xml: " + outputFile.getAbsolutePath(),
596                                               e );
597         }
598         finally
599         {
600             IOUtil.close( reader );
601         }
602     }
603 
604     private int countViolations( XmlPullParser xpp )
605         throws XmlPullParserException, IOException
606     {
607         int count = 0;
608         RuleUtil.Matcher[] ignores =
609             ( violationIgnore == null ) ? null : RuleUtil.parseMatchers( violationIgnore.split( "," ) );
610 
611         int eventType = xpp.getEventType();
612         String file = "";
613         while ( eventType != XmlPullParser.END_DOCUMENT )
614         {
615             if ( eventType == XmlPullParser.START_TAG )
616             {
617                 if ( "file".equals( xpp.getName() ) )
618                 {
619                     file = xpp.getAttributeValue( "", "name" );
620                     file = file.substring( file.lastIndexOf( File.separatorChar ) + 1 );
621                 }
622                 else if ( "error".equals( xpp.getName() ) )
623                 {
624                     String severity = xpp.getAttributeValue( "", "severity" );
625                     String source = xpp.getAttributeValue( "", "source" );
626 
627                     if ( isViolation( severity ) && !ignore( ignores, source ) )
628                     {
629                         count++;
630 
631                         if ( logViolationsToConsole )
632                         {
633                             String line = xpp.getAttributeValue( "", "line" );
634                             String column = xpp.getAttributeValue( "", "column" );
635                             String message = xpp.getAttributeValue( "", "message" );
636                             String rule = RuleUtil.getName( source );
637                             String category = RuleUtil.getCategory( source );
638 
639                             String logMessage =
640                                 file + '[' + line + ( ( column == null ) ? "" : ( ':' + column ) ) + "] (" + category
641                                     + ") " + rule + ": " + message;
642                             if ( "info".equals( severity ) )
643                             {
644                                 getLog().info( logMessage );
645                             }
646                             else if ( "warning".equals( severity ) )
647                             {
648                                 getLog().warn( logMessage );
649                             }
650                             else
651                             {
652                                 getLog().error( logMessage );
653                             }
654                         }
655                     }
656                 }
657             }
658             eventType = xpp.next();
659         }
660 
661         return count;
662     }
663 
664     /**
665      * Checks if the given severity is considered a violation.
666      *
667      * @param severity The severity to check
668      * @return <code>true</code> if the given severity is a violation, otherwise <code>false</code>
669      */
670     private boolean isViolation( String severity )
671     {
672         if ( "error".equals( severity ) )
673         {
674             return "error".equals( violationSeverity ) || "warning".equals( violationSeverity )
675                 || "info".equals( violationSeverity );
676         }
677         else if ( "warning".equals( severity ) )
678         {
679             return "warning".equals( violationSeverity ) || "info".equals( violationSeverity );
680         }
681         else if ( "info".equals( severity ) )
682         {
683             return "info".equals( violationSeverity );
684         }
685         else
686         {
687             return false;
688         }
689     }
690 
691     private boolean ignore( RuleUtil.Matcher[] ignores, String source )
692     {
693         if ( ignores != null )
694         {
695             for ( RuleUtil.Matcher ignore : ignores )
696             {
697                 if ( ignore.match( source ) )
698                 {
699                     return true;
700                 }
701             }
702         }
703 
704         return false;
705     }
706 
707     private DefaultLogger getConsoleListener()
708         throws MojoExecutionException
709     {
710         DefaultLogger consoleListener;
711 
712         if ( useFile == null )
713         {
714             stringOutputStream = new ByteArrayOutputStream();
715             consoleListener = new DefaultLogger( stringOutputStream, false );
716         }
717         else
718         {
719             OutputStream out = getOutputStream( useFile );
720 
721             consoleListener = new DefaultLogger( out, true );
722         }
723 
724         return consoleListener;
725     }
726 
727     private OutputStream getOutputStream( File file )
728         throws MojoExecutionException
729     {
730         File parentFile = file.getAbsoluteFile().getParentFile();
731 
732         if ( !parentFile.exists() )
733         {
734             parentFile.mkdirs();
735         }
736 
737         FileOutputStream fileOutputStream;
738         try
739         {
740             fileOutputStream = new FileOutputStream( file );
741         }
742         catch ( FileNotFoundException e )
743         {
744             throw new MojoExecutionException( "Unable to create output stream: " + file, e );
745         }
746         return fileOutputStream;
747     }
748 
749     private AuditListener getListener()
750         throws MojoFailureException, MojoExecutionException
751     {
752         AuditListener listener = null;
753 
754         if ( StringUtils.isNotEmpty( outputFileFormat ) )
755         {
756             File resultFile = outputFile;
757 
758             OutputStream out = getOutputStream( resultFile );
759 
760             if ( "xml".equals( outputFileFormat ) )
761             {
762                 listener = new XMLLogger( out, true );
763             }
764             else if ( "plain".equals( outputFileFormat ) )
765             {
766                 listener = new DefaultLogger( out, true );
767             }
768             else
769             {
770                 throw new MojoFailureException( "Invalid output file format: (" + outputFileFormat
771                     + "). Must be 'plain' or 'xml'." );
772             }
773         }
774 
775         return listener;
776     }
777     
778     @SuppressWarnings( "unchecked" )
779     private List<Artifact> collectArtifacts( String hint )
780     {
781         if ( plugin == null || plugin.getGroupId() == null )
782         {
783             // Maven 2.x workaround
784             plugin = mojoExecution.getMojoDescriptor().getPluginDescriptor();
785         }
786         
787         List<Artifact> artifacts = new ArrayList<Artifact>();
788 
789         PluginManagement pluginManagement = project.getBuild().getPluginManagement();
790         if ( pluginManagement != null )
791         {
792             artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( pluginManagement.getPluginsAsMap(), hint ) );
793         }
794 
795         artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( project.getBuild().getPluginsAsMap(), hint ) );
796 
797         return artifacts;
798     }
799 
800     private List<Artifact> getCheckstylePluginDependenciesAsArtifacts( Map<String, Plugin> plugins, String hint )
801     {
802         List<Artifact> artifacts = new ArrayList<Artifact>();
803         
804         Plugin checkstylePlugin = plugins.get( plugin.getGroupId() + ":" + plugin.getArtifactId() );
805         if ( checkstylePlugin != null )
806         {
807             for ( Dependency dep : checkstylePlugin.getDependencies() )
808             {
809              // @todo if we can filter on hints, it should be done here...
810                 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
811                 artifacts.add( (Artifact) plugin.getArtifactMap().get( depKey ) );
812             }
813         }
814         return artifacts;
815     }
816     
817     private List<File> getSourceDirectories()
818     {
819         List<File> sourceDirs = null;
820         // if sourceDirectory is explicitly set, use it
821         if( sourceDirectory != null )
822         {
823             sourceDirs = Collections.singletonList( sourceDirectory );
824         }
825         else
826         {
827             sourceDirs = new ArrayList<File>( sourceDirectories.size() );
828             for ( String sourceDir : sourceDirectories )
829             {
830                 sourceDirs.add( FileUtils.resolveFile( project.getBasedir(), sourceDir ) );
831             }
832         }
833         
834         return sourceDirs;
835     }
836     
837     private List<File> getTestSourceDirectories()
838     {
839         List<File> testSourceDirs = null;
840         // if testSourceDirectory is explicitly set, use it
841         if( testSourceDirectory != null )
842         {
843             testSourceDirs = Collections.singletonList( testSourceDirectory );
844         }
845         // probably null-check only required due to MavenProjectStubs
846         else if ( testSourceDirectories != null )
847         {
848             testSourceDirs = new ArrayList<File>( testSourceDirectories.size() );
849             for ( String testSourceDir : testSourceDirectories )
850             {
851                 testSourceDirs.add( FileUtils.resolveFile( project.getBasedir(), testSourceDir ) );
852             }
853         }
854         
855         return testSourceDirs;
856     }
857     
858 }