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