View Javadoc
1   package org.apache.maven.plugins.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.FileInputStream;
24  import java.io.IOException;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  import java.util.Properties;
30  import java.util.Set;
31  
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugins.pmd.model.Violation;
35  
36  import net.sourceforge.pmd.RuleViolation;
37  
38  /**
39   * This class contains utility for loading property files, which define which PMD violations
40   * from which classes should be ignored and not cause a failure.
41   * See property <code>pmd.excludeFromFailureFile</code>.
42   *
43   * @author Andreas Dangel
44   */
45  public class ExcludeViolationsFromFile implements ExcludeFromFile<Violation>
46  {
47      private Map<String, Set<String>> excludeFromFailureClasses = new HashMap<>();
48  
49      @Override
50      public void loadExcludeFromFailuresData( final String excludeFromFailureFile )
51          throws MojoExecutionException
52      {
53          if ( StringUtils.isEmpty( excludeFromFailureFile ) )
54          {
55              return;
56          }
57  
58          File file = new File( excludeFromFailureFile );
59          if ( !file.exists() )
60          {
61              return;
62          }
63          final Properties props = new Properties();
64          try ( FileInputStream fileInputStream = new FileInputStream( new File( excludeFromFailureFile ) ) )
65          {
66              props.load( fileInputStream );
67          }
68          catch ( final IOException e )
69          {
70              throw new MojoExecutionException( "Cannot load properties file " + excludeFromFailureFile, e );
71          }
72          for ( final Entry<Object, Object> propEntry : props.entrySet() )
73          {
74              final Set<String> excludedRuleSet = new HashSet<>();
75              final String className = propEntry.getKey().toString();
76              final String[] excludedRules = propEntry.getValue().toString().split( "," );
77              for ( final String excludedRule : excludedRules )
78              {
79                  excludedRuleSet.add( excludedRule.trim() );
80              }
81              excludeFromFailureClasses.put( className, excludedRuleSet );
82          }
83      }
84  
85      @Override
86      public boolean isExcludedFromFailure( final Violation errorDetail )
87      {
88          final String className = extractClassName( errorDetail.getViolationPackage(), errorDetail.getViolationClass(),
89                  errorDetail.getFileName() );
90          return isExcludedFromFailure( className, errorDetail.getRule() );
91      }
92  
93      /**
94       * Checks whether the given {@link RuleViolation} is excluded. Note: the exclusions must have been
95       * loaded before via {@link #loadExcludeFromFailuresData(String)}.
96       *
97       * @param errorDetail the violation to check
98       * @return <code>true</code> if the violation should be excluded, <code>false</code> otherwise.
99       */
100     public boolean isExcludedFromFailure( final RuleViolation errorDetail )
101     {
102         final String className = extractClassName( errorDetail.getPackageName(), errorDetail.getClassName(),
103                 errorDetail.getFilename() );
104         return isExcludedFromFailure( className, errorDetail.getRule().getName() );
105     }
106 
107     @Override
108     public int countExclusions()
109     {
110         int result = 0;
111         for ( Set<String> rules : excludeFromFailureClasses.values() )
112         {
113             result += rules.size();
114         }
115         return result;
116     }
117 
118     private boolean isExcludedFromFailure( String className, String ruleName )
119     {
120         final Set<String> excludedRuleSet = excludeFromFailureClasses.get( className );
121         return excludedRuleSet != null && excludedRuleSet.contains( ruleName );
122     }
123 
124     private String extractClassName( String packageName, String className, String fullPath )
125     {
126         // for some reason, some violations don't contain the package name, so we have to guess the full class name
127         // this looks like a bug in PMD - at least for UnusedImport rule.
128         if ( StringUtils.isNotEmpty( packageName ) && StringUtils.isNotEmpty( className ) )
129         {
130             return packageName + "." + className;
131         }
132         else if ( StringUtils.isNotEmpty( packageName ) )
133         {
134             String fileName = fullPath;
135             fileName = fileName.substring( fileName.lastIndexOf( File.separatorChar ) + 1 );
136             fileName = fileName.substring( 0, fileName.length() - 5 );
137             return packageName + "." + fileName;
138         }
139         else
140         {
141             final String fileName = fullPath;
142             final int javaIdx = fileName.indexOf( File.separator + "java" + File.separator );
143             return fileName.substring( javaIdx >= 0 ? javaIdx + 6 : 0, fileName.length() - 5 ).replace(
144                 File.separatorChar, '.' );
145         }
146     }
147 }