View Javadoc

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.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.maven.plugin.AbstractMojo;
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.MojoFailureException;
34  import org.apache.maven.project.MavenProject;
35  import org.codehaus.plexus.util.IOUtil;
36  import org.codehaus.plexus.util.ReaderFactory;
37  import org.codehaus.plexus.util.StringUtils;
38  import org.codehaus.plexus.util.xml.pull.MXParser;
39  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
40  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
41  
42  /**
43   * Base class for mojos that check if there were any PMD violations.
44   *
45   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
46   * @version $Id: AbstractPmdViolationCheckMojo.html 816688 2012-05-08 15:14:44Z hboutemy $
47   */
48  public abstract class AbstractPmdViolationCheckMojo
49      extends AbstractMojo
50  {
51      private static final Boolean FAILURES_KEY = Boolean.TRUE;
52  
53      private static final Boolean WARNINGS_KEY = Boolean.FALSE;
54  
55      /**
56       * The location of the XML report to check, as generated by the PMD report.
57       *
58       * @parameter expression="${project.build.directory}"
59       * @required
60       */
61      private File targetDirectory;
62  
63      /**
64       * Whether to fail the build if the validation check fails.
65       *
66       * @parameter expression="${pmd.failOnViolation}" default-value="true"
67       * @required
68       */
69      private boolean failOnViolation;
70  
71      /**
72       * The project language, for determining whether to run the report.
73       *
74       * @parameter expression="${project.artifact.artifactHandler.language}"
75       * @required
76       * @readonly
77       */
78      private String language;
79  
80      /**
81       * Whether to build an aggregated report at the root, or build individual reports.
82       *
83       * @parameter expression="${aggregate}" default-value="false"
84       * @since 2.2
85       */
86      protected boolean aggregate;
87  
88      /**
89       * Print details of check failures to build output.
90       *
91       * @parameter expression="${pmd.verbose}" default-value="false"
92       */
93      private boolean verbose;
94  
95      /**
96       * The project to analyze.
97       *
98       * @parameter expression="${project}"
99       * @required
100      * @readonly
101      */
102     protected MavenProject project;
103 
104     protected void executeCheck( String filename, String tagName, String key, int failurePriority )
105         throws MojoFailureException, MojoExecutionException
106     {
107         if ( aggregate && !project.isExecutionRoot() )
108         {
109             return;
110         }
111 
112         if ( "java".equals( language ) || aggregate )
113         {
114             File outputFile = new File( targetDirectory, filename );
115 
116             if ( outputFile.exists() )
117             {
118                 Reader reader = null;
119                 try
120                 {
121                     XmlPullParser xpp = new MXParser();
122                     reader = ReaderFactory.newXmlReader( outputFile );
123                     xpp.setInput( reader );
124 
125                     Map violations = getViolations( xpp, tagName, failurePriority );
126 
127                     List failures = (List) violations.get( FAILURES_KEY );
128                     List warnings = (List) violations.get( WARNINGS_KEY );
129 
130                     if ( verbose )
131                     {
132                         printErrors( failures, warnings );
133                     }
134 
135                     int failureCount = failures.size();
136                     int warningCount = warnings.size();
137 
138                     String message = getMessage( failureCount, warningCount, key, outputFile );
139 
140                     if ( failureCount > 0 && failOnViolation )
141                     {
142                         throw new MojoFailureException( message );
143                     }
144 
145                     this.getLog().info( message );
146                 }
147                 catch ( IOException e )
148                 {
149                     throw new MojoExecutionException(
150                                                       "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
151                                                       e );
152                 }
153                 catch ( XmlPullParserException e )
154                 {
155                     throw new MojoExecutionException(
156                                                       "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
157                                                       e );
158                 }
159                 finally
160                 {
161                     IOUtil.close( reader );
162                 }
163             }
164             else
165             {
166                 throw new MojoFailureException( "Unable to perform check, " + "unable to find " + outputFile );
167             }
168         }
169     }
170 
171     /**
172      * Method for collecting the violations found by the PMD tool
173      *
174      * @param xpp
175      *            the xml parser object
176      * @param tagName
177      *            the element that will be checked
178      * @return an int that specifies the number of violations found
179      * @throws XmlPullParserException
180      * @throws IOException
181      */
182     private Map getViolations( XmlPullParser xpp, String tagName, int failurePriority )
183         throws XmlPullParserException, IOException
184     {
185         int eventType = xpp.getEventType();
186 
187         List failures = new ArrayList();
188         List warnings = new ArrayList();
189 
190         String fullpath = null;
191 
192         while ( eventType != XmlPullParser.END_DOCUMENT )
193         {
194             if ( eventType == XmlPullParser.START_TAG && "file".equals( xpp.getName() ) )
195             {
196                 fullpath = xpp.getAttributeValue( "", "name" );
197             }
198             if ( eventType == XmlPullParser.START_TAG && tagName.equals( xpp.getName() ) )
199             {
200                 Map details = getErrorDetails( xpp );
201 
202                 if ( fullpath != null )
203                 {
204                     details.put( "filename", getFilename( fullpath, (String) details.get( "package" ) ) );
205                 }
206 
207                 try
208                 {
209                     int priority = Integer.parseInt( (String) details.get( "priority" ) );
210                     if ( priority <= failurePriority )
211                     {
212                         failures.add( details );
213                     }
214                     else
215                     {
216                         warnings.add( details );
217                     }
218                 }
219                 catch ( NumberFormatException e )
220                 {
221                     // I don't know what priority this is. Treat it like a
222                     // failure
223                     failures.add( details );
224                 }
225                 catch ( NullPointerException e )
226                 {
227                     // I don't know what priority this is. Treat it like a
228                     // failure
229                     failures.add( details );
230                 }
231 
232             }
233 
234             eventType = xpp.next();
235         }
236 
237         HashMap map = new HashMap( 2 );
238         map.put( FAILURES_KEY, failures );
239         map.put( WARNINGS_KEY, warnings );
240         return map;
241     }
242 
243     private String getFilename( String fullpath, String pkg )
244     {
245         int index = fullpath.lastIndexOf( File.separatorChar );
246 
247         while ( StringUtils.isNotEmpty( pkg ) )
248         {
249             index = fullpath.substring( 0, index ).lastIndexOf( File.separatorChar );
250 
251             int dot = pkg.indexOf( '.' );
252 
253             if ( dot < 0 )
254             {
255                 break;
256             }
257             pkg = pkg.substring( dot + 1 );
258         }
259 
260         return fullpath.substring( index + 1 );
261     }
262 
263     /**
264      * Prints the warnings and failures
265      *
266      * @param failures
267      *            list of failures
268      * @param warnings
269      *            list of warnings
270      */
271     protected void printErrors( List failures, List warnings )
272     {
273         Iterator iter = warnings.iterator();
274         while ( iter.hasNext() )
275         {
276             printError( (Map) iter.next(), "Warning" );
277         }
278 
279         iter = failures.iterator();
280         while ( iter.hasNext() )
281         {
282             printError( (Map) iter.next(), "Failure" );
283         }
284     }
285 
286     /**
287      * Gets the output message
288      *
289      * @param failureCount
290      * @param warningCount
291      * @param key
292      * @param outputFile
293      * @return
294      */
295     private String getMessage( int failureCount, int warningCount, String key, File outputFile )
296     {
297         StringBuffer message = new StringBuffer();
298         if ( failureCount > 0 || warningCount > 0 )
299         {
300             if ( failureCount > 0 )
301             {
302                 message.append( "You have " + failureCount + " " + key + ( failureCount > 1 ? "s" : "" ) );
303             }
304 
305             if ( warningCount > 0 )
306             {
307                 if ( failureCount > 0 )
308                 {
309                     message.append( " and " );
310                 }
311                 else
312                 {
313                     message.append( "You have " );
314                 }
315                 message.append( warningCount + " warning" + ( warningCount > 1 ? "s" : "" ) );
316             }
317 
318             message.append( ". For more details see:" + outputFile.getAbsolutePath() );
319         }
320         return message.toString();
321     }
322 
323     /**
324      * Formats the failure details and prints them as an INFO message
325      *
326      * @param item
327      */
328     protected abstract void printError( Map item, String severity );
329 
330     /**
331      * Gets the attributes and text for the violation tag and puts them in a
332      * HashMap
333      *
334      * @param xpp
335      * @throws XmlPullParserException
336      * @throws IOException
337      */
338     protected abstract Map getErrorDetails( XmlPullParser xpp )
339         throws XmlPullParserException, IOException;
340 }