View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.pmd;
20  
21  import java.io.FileReader;
22  import java.io.IOException;
23  import java.io.LineNumberReader;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import net.sourceforge.pmd.cpd.Mark;
30  import net.sourceforge.pmd.cpd.Match;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugins.pmd.model.CpdFile;
33  import org.apache.maven.plugins.pmd.model.Duplication;
34  
35  /**
36   * This class contains utility methods to load property files which define which files
37   * should be excluded from the CPD duplication results.
38   * See property <code>pmd.excludeFromFailureFile</code>.
39   *
40   * @author Andreas Dangel
41   */
42  public class ExcludeDuplicationsFromFile implements ExcludeFromFile<Duplication> {
43  
44      private final List<Set<String>> exclusionList = new ArrayList<>();
45  
46      @Override
47      public boolean isExcludedFromFailure(final Duplication errorDetail) {
48          final Set<String> uniquePaths = new HashSet<>();
49          for (final CpdFile cpdFile : errorDetail.getFiles()) {
50              uniquePaths.add(cpdFile.getPath());
51          }
52          return isExcludedFromFailure(uniquePaths);
53      }
54  
55      /**
56       * Checks whether the given {@link Match} is excluded.
57       * Note: The exclusion must have been loaded before via {@link #loadExcludeFromFailuresData(String)}.
58       *
59       * @param errorDetail the duplication to check
60       * @return <code>true</code> if the given duplication should be excluded, <code>false</code> otherwise.
61       */
62      public boolean isExcludedFromFailure(final Match errorDetail) {
63          final Set<String> uniquePaths = new HashSet<>();
64          for (Mark mark : errorDetail.getMarkSet()) {
65              uniquePaths.add(mark.getFilename());
66          }
67          return isExcludedFromFailure(uniquePaths);
68      }
69  
70      private boolean isExcludedFromFailure(Set<String> uniquePaths) {
71          for (final Set<String> singleExclusionGroup : exclusionList) {
72              if (uniquePaths.size() == singleExclusionGroup.size()
73                      && duplicationExcludedByGroup(uniquePaths, singleExclusionGroup)) {
74                  return true;
75              }
76          }
77          return false;
78      }
79  
80      private boolean duplicationExcludedByGroup(final Set<String> uniquePaths, final Set<String> singleExclusionGroup) {
81          for (final String path : uniquePaths) {
82              if (!fileExcludedByGroup(path, singleExclusionGroup)) {
83                  return false;
84              }
85          }
86          return true;
87      }
88  
89      private boolean fileExcludedByGroup(final String path, final Set<String> singleExclusionGroup) {
90          final String formattedPath = path.replace('\\', '.').replace('/', '.');
91          for (final String className : singleExclusionGroup) {
92              if (formattedPath.contains(className)) {
93                  return true;
94              }
95          }
96          return false;
97      }
98  
99      @Override
100     public void loadExcludeFromFailuresData(final String excludeFromFailureFile) throws MojoExecutionException {
101         if (excludeFromFailureFile == null || excludeFromFailureFile.isEmpty()) {
102             return;
103         }
104 
105         try (LineNumberReader reader = new LineNumberReader(new FileReader(excludeFromFailureFile))) {
106             String line;
107             while ((line = reader.readLine()) != null) {
108                 if (!line.startsWith("#")) {
109                     exclusionList.add(createSetFromExclusionLine(line));
110                 }
111             }
112         } catch (final IOException e) {
113             throw new MojoExecutionException("Cannot load file " + excludeFromFailureFile, e);
114         }
115     }
116 
117     private Set<String> createSetFromExclusionLine(final String line) {
118         final Set<String> result = new HashSet<>();
119         for (final String className : line.split(",")) {
120             result.add(className.trim());
121         }
122         return result;
123     }
124 
125     @Override
126     public int countExclusions() {
127         return exclusionList.size();
128     }
129 }