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.buildcache.checksum.exclude;
20  
21  import java.nio.file.FileSystems;
22  import java.nio.file.Path;
23  import java.nio.file.PathMatcher;
24  import java.nio.file.Paths;
25  
26  import org.apache.commons.io.FilenameUtils;
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.maven.buildcache.xml.config.Exclude;
29  
30  public class Exclusion {
31  
32      /**
33       * Glob prefix for path matchers
34       */
35      private static final String GLOB_PX = "glob:";
36  
37      private static final String GLOB_ALL_PATHS = "**";
38      private static final String GLOB_ALL_NAMES = "*";
39  
40      /**
41       * Default glob
42       */
43      private static final String DEFAULT_GLOB = GLOB_ALL_PATHS;
44  
45      private final Path absolutePath;
46      private final PathMatcher matcher;
47  
48      private final MatcherType matcherType;
49  
50      private final EntryType entryType;
51  
52      /**
53       * Denormalization to increase pathmatching resolution speed if the glob obviously match all files
54       */
55      private boolean matchesAllNames;
56  
57      /**
58       * Denormalization to increase pathmatching resolution speed if the glob obviously match all paths
59       */
60      private boolean matchesAllPaths;
61  
62      /**
63       * True if the configured value was already an absolute path
64       */
65      private final boolean configuredAsAbsolute;
66  
67      public Exclusion(Path basedir, Exclude exclude) {
68  
69          if (StringUtils.isNotBlank(exclude.getValue())) {
70              Path candidate = Paths.get(FilenameUtils.separatorsToSystem(exclude.getValue()));
71              configuredAsAbsolute = candidate.isAbsolute();
72              Path resolvedPath = configuredAsAbsolute ? candidate : basedir.resolve(candidate);
73              this.absolutePath = resolvedPath.toAbsolutePath().normalize();
74          } else {
75              configuredAsAbsolute = false;
76              this.absolutePath = basedir;
77          }
78          // Unix style glob is correctly interpreted on windows by the corresponding pathMatcher implementation.
79          String unixStyleGlob = convertGlobToUnixStyle(exclude.getGlob());
80          this.matcher = FileSystems.getDefault().getPathMatcher(GLOB_PX + unixStyleGlob);
81          this.matcherType = MatcherType.valueOf(exclude.getMatcherType().toUpperCase());
82          this.entryType = EntryType.valueOf(exclude.getEntryType().toUpperCase());
83          computeMatcherDenormalization(unixStyleGlob);
84      }
85  
86      public Exclusion(Path absolutePath, MatcherType resolutionType, EntryType entryType) {
87          this.configuredAsAbsolute = false;
88          this.absolutePath = absolutePath;
89          this.matcher = absolutePath.getFileSystem().getPathMatcher(GLOB_PX + DEFAULT_GLOB);
90          this.matcherType = resolutionType;
91          this.entryType = entryType;
92          computeMatcherDenormalization(DEFAULT_GLOB);
93      }
94  
95      private String convertGlobToUnixStyle(String glob) {
96          return glob.replace("\\\\", "/");
97      }
98  
99      private void computeMatcherDenormalization(String glob) {
100         if (GLOB_ALL_PATHS.equals(glob)) {
101             matchesAllPaths = true;
102         } else if (GLOB_ALL_NAMES.equals(glob)) {
103             matchesAllNames = true;
104         }
105     }
106 
107     public Path getAbsolutePath() {
108         return absolutePath;
109     }
110 
111     public EntryType getEntryType() {
112         return entryType;
113     }
114 
115     /**
116      * True if the exclusion applies to the given path (does not indicate that the path is excluded)
117      * @param path a visited path
118      * @return true if the exclusion applies to the given path
119      */
120     private boolean applies(Path path) {
121         return path.startsWith(this.absolutePath);
122     }
123 
124     public boolean excludesPath(Path parentPath, Path path) {
125         if (applies(path)) {
126             switch (matcherType) {
127                 case FILENAME:
128                     if (matchesAllPaths || matchesAllNames || matcher.matches(path.getFileName())) {
129                         return true;
130                     }
131                     break;
132                 case PATH:
133                     // If path is configured relative, matching has to be done relatively to the project directory in
134                     // order to be independent from
135                     // the project location on the disk.
136                     if (matchesAllPaths || matcher.matches(configuredAsAbsolute ? path : parentPath.relativize(path))) {
137                         return true;
138                     }
139                     break;
140                 default:
141                     throw new RuntimeException("Exclusion resolution type not handled.");
142             }
143         }
144         return false;
145     }
146 
147     public enum MatcherType {
148         FILENAME,
149         PATH;
150     }
151 
152     public enum EntryType {
153         FILE,
154         DIRECTORY,
155         ALL;
156     }
157 }