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 org.apache.maven.plugin.AbstractMojo;
23  import org.apache.maven.plugin.MojoExecutionException;
24  import org.apache.maven.plugin.MojoFailureException;
25  import org.codehaus.plexus.util.ReaderFactory;
26  import org.codehaus.plexus.util.xml.pull.MXParser;
27  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
28  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
29  
30  import java.io.BufferedReader;
31  import java.io.File;
32  import java.io.IOException;
33  import java.io.Reader;
34  
35  /**
36   * Perform a violation check against the last Checkstyle run to see if there are
37   * any violations. It reads the Checkstyle output file, counts the number of
38   * violations found and displays it on the console.
39   *
40   * @author <a href="mailto:joakim@erdfelt.net">Joakim Erdfelt</a>
41   * @goal check
42   * @phase verify
43   * @execute goal="checkstyle"
44   * @requiresDependencyResolution compile
45   */
46  public class CheckstyleViolationCheckMojo
47      extends AbstractMojo
48  {
49      /**
50       * Specifies the path and filename to save the Checkstyle output. The format
51       * of the output file is determined by the <code>outputFileFormat</code>
52       * parameter.
53       *
54       * @parameter expression="${checkstyle.output.file}"
55       *            default-value="${project.build.directory}/checkstyle-result.xml"
56       */
57      private File outputFile;
58  
59      /**
60       * Specifies the format of the output to be used when writing to the output
61       * file. Valid values are "plain" and "xml".
62       *
63       * @parameter expression="${checkstyle.output.format}" default-value="xml"
64       */
65      private String outputFileFormat;
66  
67      /**
68       * Do we fail the build on a violation?
69       *
70       * @parameter expression="${checkstyle.failOnViolation}"
71       *            default-value="true"
72       */
73      private boolean failOnViolation;
74  
75      /**
76       * The maximum number of allowed violations. The execution fails only if the
77       * number of violations is above this limit.
78       *
79       * @parameter expression="${checkstyle.maxAllowedViolations}" default-value="0"
80       * @since 2.3
81       */
82      private int maxAllowedViolations = 0;
83  
84      /**
85       * The lowest severity level that is considered a violation.
86       * Valid values are "error", "warning" and "info".
87       *
88       * @parameter expression="${checkstyle.violationSeverity}" default-value="error"
89       * @since 2.2
90       */
91      private String violationSeverity = "error";
92  
93      /**
94       * Skip entire check.
95       *
96       * @parameter expression="${checkstyle.skip}" default-value="false"
97       * @since 2.2
98       */
99      private boolean skip;
100 
101     /**
102      * Output the detected violations to the console.
103      *
104      * @parameter expression="${checkstyle.console}" default-value="false"
105      * @since 2.3
106      */
107     private boolean logViolationsToConsole;
108 
109     /**
110      * @see org.apache.maven.plugin.Mojo#execute()
111      */
112     public void execute()
113         throws MojoExecutionException, MojoFailureException
114     {
115         if ( !skip )
116         {
117             if ( !"xml".equals( outputFileFormat ) )
118             {
119                 throw new MojoExecutionException( "Output format is '" + outputFileFormat
120                     + "', checkstyle:check requires format to be 'xml'." );
121             }
122 
123             if ( !outputFile.exists() )
124             {
125                 getLog().info(
126                                "Unable to perform checkstyle:check, "
127                                    + "unable to find checkstyle:checkstyle outputFile." );
128                 return;
129             }
130 
131             try
132             {
133                 XmlPullParser xpp = new MXParser();
134                 Reader freader = ReaderFactory.newXmlReader( outputFile );
135                 BufferedReader breader = new BufferedReader( freader );
136                 xpp.setInput( breader );
137 
138                 int violations = countViolations( xpp );
139                 if ( violations > maxAllowedViolations )
140                 {
141                     if ( failOnViolation )
142                     {
143                         String msg = "You have " + violations + " Checkstyle violation"
144                             + ( ( violations > 1 ) ? "s" : "" ) + ".";
145                         if ( maxAllowedViolations > 0 )
146                         {
147                             msg += " The maximum number of allowed violations is " + maxAllowedViolations + ".";
148                         }
149                         throw new MojoFailureException( msg );
150                     }
151 
152                     getLog().warn( "checkstyle:check violations detected but failOnViolation set to false" );
153                 }
154             }
155             catch ( IOException e )
156             {
157                 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
158                     + outputFile.getAbsolutePath(), e );
159             }
160             catch ( XmlPullParserException e )
161             {
162                 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
163                     + outputFile.getAbsolutePath(), e );
164             }
165         }
166     }
167 
168     private int countViolations( XmlPullParser xpp )
169         throws XmlPullParserException, IOException
170     {
171         int count = 0;
172 
173         int eventType = xpp.getEventType();
174         String file = "";
175         while ( eventType != XmlPullParser.END_DOCUMENT )
176         {
177             if ( eventType == XmlPullParser.START_TAG && "file".equals( xpp.getName() ) )
178             {
179                 file = xpp.getAttributeValue( "", "name" );
180                 file = file.substring( file.lastIndexOf( File.separatorChar ) + 1 );
181             }
182 
183             if ( eventType == XmlPullParser.START_TAG && "error".equals( xpp.getName() )
184                 && isViolation( xpp.getAttributeValue( "", "severity" ) ) )
185             {
186                 if ( logViolationsToConsole )
187                 {
188                     StringBuffer stb = new StringBuffer();
189                     stb.append( file );
190                     stb.append( '[' );
191                     stb.append( xpp.getAttributeValue( "", "line" ) );
192                     stb.append( ':' );
193                     stb.append( xpp.getAttributeValue( "", "column" ) );
194                     stb.append( "] " );
195                     stb.append( xpp.getAttributeValue( "", "message" ) );
196                     getLog().error( stb.toString() );
197                 }
198                 count++;
199             }
200             eventType = xpp.next();
201         }
202 
203         return count;
204     }
205 
206     /**
207      * Checks if the given severity is considered a violation.
208      *
209      * @param severity The severity to check
210      * @return <code>true</code> if the given severity is a violation, otherwise <code>false</code>
211      */
212     private boolean isViolation( String severity )
213     {
214         if ( "error".equals( severity ) )
215         {
216             return "error".equals( violationSeverity ) || "warning".equals( violationSeverity )
217                 || "info".equals( violationSeverity );
218         }
219         else if ( "warning".equals( severity ) )
220         {
221             return "warning".equals( violationSeverity ) || "info".equals( violationSeverity );
222         }
223         else if ( "info".equals( severity ) )
224         {
225             return "info".equals( violationSeverity );
226         }
227         else
228         {
229             return false;
230         }
231     }
232 }