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