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