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 org.apache.maven.plugin.MojoExecutionException;
23  import org.apache.maven.plugin.MojoFailureException;
24  import org.apache.maven.plugin.pmd.model.PmdErrorDetail;
25  import org.apache.maven.plugin.pmd.model.PmdFile;
26  import org.apache.maven.plugin.pmd.model.Violation;
27  import org.apache.maven.plugin.pmd.model.io.xpp3.PmdXpp3Reader;
28  import org.apache.maven.plugins.annotations.Execute;
29  import org.apache.maven.plugins.annotations.LifecyclePhase;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.codehaus.plexus.util.IOUtil;
33  import org.codehaus.plexus.util.StringUtils;
34  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
35  
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.FileReader;
39  import java.io.IOException;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Map.Entry;
46  import java.util.Properties;
47  import java.util.Set;
48  
49  /**
50   * Fail the build if there were any PMD violations in the source code.
51   *
52   * @version $Id: PmdViolationCheckMojo.html 853015 2013-03-04 21:10:54Z olamy $
53   * @since 2.0
54   */
55  @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true )
56  @Execute( goal = "pmd" )
57  public class PmdViolationCheckMojo
58      extends AbstractPmdViolationCheckMojo<Violation>
59  {
60      /**
61       * What priority level to fail the build on. Failures at or above this level
62       * will stop the build. Anything below will be warnings and will be
63       * displayed in the build output if verbose=true. Note: Minimum Priority = 5
64       * Maximum Priority = 0
65       */
66      @Parameter( property = "pmd.failurePriority", defaultValue = "5", required = true )
67      private int failurePriority;
68  
69      /**
70       * Skip the PMD checks.  Most useful on the command line
71       * via "-Dpmd.skip=true".
72       */
73      @Parameter( property = "pmd.skip", defaultValue = "false" )
74      private boolean skip;
75  
76      private final Map<String, Set<String>> excludeFromFailureClasses = new HashMap<String, Set<String>>();
77  
78      /**
79       * {@inheritDoc}
80       */
81      public void execute()
82          throws MojoExecutionException, MojoFailureException
83      {
84          if ( !skip )
85          {
86              executeCheck( "pmd.xml", "violation", "PMD violation", failurePriority );
87          }
88      }
89  
90      @Override
91      protected void loadExcludeFromFailuresData( final String excludeFromFailureFile )
92          throws MojoExecutionException
93      {
94          File file = new File( excludeFromFailureFile );
95          if ( !file.exists() )
96          {
97              return;
98          }
99          final Properties props = new Properties();
100         FileInputStream fileInputStream = null;
101         try
102         {
103             fileInputStream = new FileInputStream( new File( excludeFromFailureFile ) );
104             props.load( fileInputStream );
105         }
106         catch ( final IOException e )
107         {
108             throw new MojoExecutionException( "Cannot load properties file " + excludeFromFailureFile, e );
109         }
110         finally
111         {
112             IOUtil.close( fileInputStream );
113         }
114         for ( final Entry<Object, Object> propEntry : props.entrySet() )
115         {
116             final Set<String> excludedRuleSet = new HashSet<String>();
117             final String className = propEntry.getKey().toString();
118             final String[] excludedRules = propEntry.getValue().toString().split( "," );
119             for ( final String excludedRule : excludedRules )
120             {
121                 excludedRuleSet.add( excludedRule.trim() );
122             }
123             excludeFromFailureClasses.put( className, excludedRuleSet );
124         }
125     }
126 
127     @Override
128     protected boolean isExcludedFromFailure( final Violation errorDetail )
129     {
130         final String className = extractClassName( errorDetail );
131         final Set<String> excludedRuleSet = excludeFromFailureClasses.get( className );
132         return excludedRuleSet != null && excludedRuleSet.contains( errorDetail.getRule() );
133     }
134 
135     private String extractClassName( final Violation errorDetail )
136     {
137         //for some reason, some violations don't contain the package name, so we have to guess the full class name
138         if ( errorDetail.getViolationPackage() != null && errorDetail.getViolationClass() != null )
139         {
140             return errorDetail.getViolationPackage() + "." + errorDetail.getViolationClass();
141         }
142         else
143         {
144             final String fileName = errorDetail.getFileName();
145             final int javaIdx = fileName.indexOf( "\\java\\" );
146             return fileName.substring( javaIdx >= 0 ? javaIdx + 6 : 0, fileName.length() - 5 ).replace( '\\', '.' );
147         }
148     }
149 
150     /**
151      * {@inheritDoc}
152      */
153     protected void printError( Violation item, String severity )
154     {
155 
156         StringBuilder buff = new StringBuilder( 100 );
157         buff.append( "PMD " + severity + ": " );
158         if ( item.getViolationClass() != null )
159         {
160             if ( item.getViolationPackage() != null )
161             {
162                 buff.append( item.getViolationPackage() );
163                 buff.append( "." );
164             }
165             buff.append( item.getViolationClass() );
166         }
167         else
168         {
169             buff.append( item.getFileName() );
170         }
171         buff.append( ":" );
172         buff.append( item.getBeginline() );
173         buff.append( " Rule:" ).append( item.getRule() );
174         buff.append( " Priority:" ).append( item.getPriority() );
175         buff.append( " " ).append( item.getText() ).append( "." );
176 
177         this.getLog().info( buff.toString() );
178     }
179 
180     @Override
181     protected List<Violation> getErrorDetails( File pmdFile )
182         throws XmlPullParserException, IOException
183     {
184         PmdXpp3Reader reader = new PmdXpp3Reader();
185         PmdErrorDetail details = reader.read( new FileReader( pmdFile ), false );
186 
187         List<Violation> violations = new ArrayList<Violation>();
188         for ( PmdFile file : details.getFiles() )
189         {
190             String fullPath = file.getName();
191 
192             for ( Violation violation : file.getViolations() )
193             {
194                 violation.setFileName( getFilename( fullPath, violation.getViolationPackage() ) );
195                 violations.add( violation );
196             }
197         }
198         return violations;
199     }
200 
201     @Override
202     protected int getPriority( Violation errorDetail )
203     {
204         return errorDetail.getPriority();
205     }
206 
207     @Override
208     protected ViolationDetails<Violation> newViolationDetailsInstance()
209     {
210         return new ViolationDetails<Violation>();
211     }
212 
213     private String getFilename( String fullpath, String pkg )
214     {
215         int index = fullpath.lastIndexOf( File.separatorChar );
216 
217         while ( StringUtils.isNotEmpty( pkg ) )
218         {
219             index = fullpath.substring( 0, index ).lastIndexOf( File.separatorChar );
220 
221             int dot = pkg.indexOf( '.' );
222 
223             if ( dot < 0 )
224             {
225                 break;
226             }
227             pkg = pkg.substring( dot + 1 );
228         }
229 
230         return fullpath.substring( index + 1 );
231     }
232 }