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 }