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