1 package org.apache.maven.plugin.pmd;
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.File;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.apache.maven.plugin.AbstractMojo;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugin.MojoFailureException;
31 import org.apache.maven.project.MavenProject;
32 import org.codehaus.plexus.util.IOUtil;
33 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
34
35 /**
36 * Base class for mojos that check if there were any PMD violations.
37 *
38 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
39 * @version $Id: AbstractPmdViolationCheckMojo.html 816696 2012-05-08 15:19:20Z hboutemy $
40 */
41 public abstract class AbstractPmdViolationCheckMojo<D>
42 extends AbstractMojo
43 {
44 /**
45 * The location of the XML report to check, as generated by the PMD report.
46 *
47 * @parameter expression="${project.build.directory}"
48 * @required
49 */
50 private File targetDirectory;
51
52 /**
53 * Whether to fail the build if the validation check fails.
54 *
55 * @parameter expression="${pmd.failOnViolation}" default-value="true"
56 * @required
57 */
58 private boolean failOnViolation;
59
60 /**
61 * The project language, for determining whether to run the report.
62 *
63 * @parameter expression="${project.artifact.artifactHandler.language}"
64 * @required
65 * @readonly
66 */
67 private String language;
68
69 /**
70 * Whether to build an aggregated report at the root, or build individual reports.
71 *
72 * @parameter expression="${aggregate}" default-value="false"
73 * @since 2.2
74 */
75 protected boolean aggregate;
76
77 /**
78 * Print details of check failures to build output.
79 *
80 * @parameter expression="${pmd.verbose}" default-value="false"
81 */
82 private boolean verbose;
83
84 /**
85 * The project to analyze.
86 *
87 * @parameter expression="${project}"
88 * @required
89 * @readonly
90 */
91 protected MavenProject project;
92
93 protected void executeCheck( String filename, String tagName, String key, int failurePriority )
94 throws MojoFailureException, MojoExecutionException
95 {
96 if ( aggregate && !project.isExecutionRoot() )
97 {
98 return;
99 }
100
101 if ( "java".equals( language ) || aggregate )
102 {
103 File outputFile = new File( targetDirectory, filename );
104
105 if ( outputFile.exists() )
106 {
107 Reader reader = null;
108 try
109 {
110 ViolationDetails<D> violations = getViolations( outputFile, failurePriority );
111
112 List<D> failures = violations.getFailureDetails();
113 List<D> warnings = violations.getWarningDetails();
114
115 if ( verbose )
116 {
117 printErrors( failures, warnings );
118 }
119
120 int failureCount = failures.size();
121 int warningCount = warnings.size();
122
123 String message = getMessage( failureCount, warningCount, key, outputFile );
124
125 if ( failureCount > 0 && failOnViolation )
126 {
127 throw new MojoFailureException( message );
128 }
129
130 this.getLog().info( message );
131 }
132 catch ( IOException e )
133 {
134 throw new MojoExecutionException(
135 "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
136 e );
137 }
138 catch ( XmlPullParserException e )
139 {
140 throw new MojoExecutionException(
141 "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
142 e );
143 }
144 finally
145 {
146 IOUtil.close( reader );
147 }
148 }
149 else
150 {
151 throw new MojoFailureException( "Unable to perform check, " + "unable to find " + outputFile );
152 }
153 }
154 }
155
156 /**
157 * Method for collecting the violations found by the PMD tool
158 *
159 * @param xpp
160 * the xml parser object
161 * @param tagName
162 * the element that will be checked
163 * @return an int that specifies the number of violations found
164 * @throws XmlPullParserException
165 * @throws IOException
166 */
167 private ViolationDetails<D> getViolations( File analysisFile, int failurePriority )
168 throws XmlPullParserException, IOException
169 {
170 List<D> failures = new ArrayList<D>();
171 List<D> warnings = new ArrayList<D>();
172
173 List<D> violations = getErrorDetails( analysisFile );
174
175 for( D violation : violations )
176 {
177 int priority = getPriority( violation );
178 if ( priority <= failurePriority )
179 {
180 failures.add( violation );
181 }
182 else
183 {
184 warnings.add( violation );
185 }
186 }
187
188 ViolationDetails<D> details = newViolationDetailsInstance();
189 details.setFailureDetails( failures );
190 details.setWarningDetails( warnings );
191 return details;
192 }
193
194 protected abstract int getPriority( D errorDetail );
195
196 protected abstract ViolationDetails<D> newViolationDetailsInstance();
197
198 /**
199 * Prints the warnings and failures
200 *
201 * @param failures
202 * list of failures
203 * @param warnings
204 * list of warnings
205 */
206 protected void printErrors( List<D> failures, List<D> warnings )
207 {
208 for ( D warning : warnings )
209 {
210 printError( warning, "Warning" );
211 }
212
213 for ( D failure : failures )
214 {
215 printError( failure, "Failure" );
216 }
217 }
218
219 /**
220 * Gets the output message
221 *
222 * @param failureCount
223 * @param warningCount
224 * @param key
225 * @param outputFile
226 * @return
227 */
228 private String getMessage( int failureCount, int warningCount, String key, File outputFile )
229 {
230 StringBuffer message = new StringBuffer();
231 if ( failureCount > 0 || warningCount > 0 )
232 {
233 if ( failureCount > 0 )
234 {
235 message.append( "You have " + failureCount + " " + key + ( failureCount > 1 ? "s" : "" ) );
236 }
237
238 if ( warningCount > 0 )
239 {
240 if ( failureCount > 0 )
241 {
242 message.append( " and " );
243 }
244 else
245 {
246 message.append( "You have " );
247 }
248 message.append( warningCount + " warning" + ( warningCount > 1 ? "s" : "" ) );
249 }
250
251 message.append( ". For more details see:" ).append( outputFile.getAbsolutePath() );
252 }
253 return message.toString();
254 }
255
256 /**
257 * Formats the failure details and prints them as an INFO message
258 *
259 * @param item
260 */
261 protected abstract void printError( D item, String severity );
262
263 /**
264 * Gets the attributes and text for the violation tag and puts them in a
265 * HashMap
266 *
267 * @param xpp
268 * @throws XmlPullParserException
269 * @throws IOException
270 */
271 protected abstract List<D> getErrorDetails( File analisysFile )
272 throws XmlPullParserException, IOException;
273 }