1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.ignore.internal;
11
12 import static org.eclipse.jgit.ignore.internal.Strings.checkWildCards;
13 import static org.eclipse.jgit.ignore.internal.Strings.count;
14 import static org.eclipse.jgit.ignore.internal.Strings.getPathSeparator;
15 import static org.eclipse.jgit.ignore.internal.Strings.isWildCard;
16 import static org.eclipse.jgit.ignore.internal.Strings.split;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import org.eclipse.jgit.errors.InvalidPatternException;
22 import org.eclipse.jgit.ignore.IMatcher;
23 import org.eclipse.jgit.ignore.internal.Strings.PatternState;
24
25
26
27
28
29
30 public class PathMatcher extends AbstractMatcher {
31
32 private static final WildMatcherher.html#WildMatcher">WildMatcher WILD_NO_DIRECTORY = new WildMatcher(false);
33
34 private static final WildMatcherr.html#WildMatcher">WildMatcher WILD_ONLY_DIRECTORY = new WildMatcher(
35 true);
36
37 private final List<IMatcher> matchers;
38
39 private final char slash;
40
41 private final boolean beginning;
42
43 private PathMatcher(String pattern, Character pathSeparator,
44 boolean dirOnly)
45 throws InvalidPatternException {
46 super(pattern, dirOnly);
47 slash = getPathSeparator(pathSeparator);
48 beginning = pattern.indexOf(slash) == 0;
49 if (isSimplePathWithSegments(pattern))
50 matchers = null;
51 else
52 matchers = createMatchers(split(pattern, slash), pathSeparator,
53 dirOnly);
54 }
55
56 private boolean isSimplePathWithSegments(String path) {
57 return !isWildCard(path) && path.indexOf('\\') < 0
58 && count(path, slash, true) > 0;
59 }
60
61 private static List<IMatcher> createMatchers(List<String> segments,
62 Character pathSeparator, boolean dirOnly)
63 throws InvalidPatternException {
64 List<IMatcher> matchers = new ArrayList<>(segments.size());
65 for (int i = 0; i < segments.size(); i++) {
66 String segment = segments.get(i);
67 IMatcher matcher = createNameMatcher0(segment, pathSeparator,
68 dirOnly, i == segments.size() - 1);
69 if (i > 0) {
70 final IMatcher last = matchers.get(matchers.size() - 1);
71 if (isWild(matcher) && isWild(last))
72
73
74 matchers.remove(matchers.size() - 1);
75 }
76
77 matchers.add(matcher);
78 }
79 return matchers;
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 public static IMatcher createPathMatcher(String pattern,
96 Character pathSeparator, boolean dirOnly)
97 throws InvalidPatternException {
98 pattern = trim(pattern);
99 char slash = Strings.getPathSeparator(pathSeparator);
100
101 int slashIdx = pattern.indexOf(slash, 1);
102 if (slashIdx > 0 && slashIdx < pattern.length() - 1)
103 return new PathMatcher(pattern, pathSeparator, dirOnly);
104 return createNameMatcher0(pattern, pathSeparator, dirOnly, true);
105 }
106
107
108
109
110
111
112
113
114
115 private static String trim(String pattern) {
116 while (pattern.length() > 0
117 && pattern.charAt(pattern.length() - 1) == ' ') {
118 if (pattern.length() > 1
119 && pattern.charAt(pattern.length() - 2) == '\\') {
120
121
122 pattern = pattern.substring(0, pattern.length() - 2) + " ";
123 return pattern;
124 }
125 pattern = pattern.substring(0, pattern.length() - 1);
126 }
127 return pattern;
128 }
129
130 private static IMatcher createNameMatcher0(String segment,
131 Character pathSeparator, boolean dirOnly, boolean lastSegment)
132 throws InvalidPatternException {
133
134 if (WildMatcher.WILDMATCH.equals(segment)
135 || WildMatcher.WILDMATCH2.equals(segment))
136 return dirOnly && lastSegment ? WILD_ONLY_DIRECTORY
137 : WILD_NO_DIRECTORY;
138
139 PatternState state = checkWildCards(segment);
140 switch (state) {
141 case LEADING_ASTERISK_ONLY:
142 return new LeadingAsteriskMatcher(segment, pathSeparator, dirOnly);
143 case TRAILING_ASTERISK_ONLY:
144 return new TrailingAsteriskMatcher(segment, pathSeparator, dirOnly);
145 case COMPLEX:
146 return new WildCardMatcher(segment, pathSeparator, dirOnly);
147 default:
148 return new NameMatcher(segment, pathSeparator, dirOnly, true);
149 }
150 }
151
152
153 @Override
154 public boolean matches(String path, boolean assumeDirectory,
155 boolean pathMatch) {
156 if (matchers == null) {
157 return simpleMatch(path, assumeDirectory, pathMatch);
158 }
159 return iterate(path, 0, path.length(), assumeDirectory, pathMatch);
160 }
161
162
163
164
165
166
167 private boolean simpleMatch(String path, boolean assumeDirectory,
168 boolean pathMatch) {
169 boolean hasSlash = path.indexOf(slash) == 0;
170 if (beginning && !hasSlash) {
171 path = slash + path;
172 }
173 if (!beginning && hasSlash) {
174 path = path.substring(1);
175 }
176 if (path.equals(pattern)) {
177
178 return !dirOnly || assumeDirectory;
179 }
180
181
182
183
184
185 String prefix = pattern + slash;
186 if (pathMatch) {
187 return path.equals(prefix) && (!dirOnly || assumeDirectory);
188 }
189 if (path.startsWith(prefix)) {
190 return true;
191 }
192 return false;
193 }
194
195
196 @Override
197 public boolean matches(String segment, int startIncl, int endExcl) {
198 throw new UnsupportedOperationException(
199 "Path matcher works only on entire paths");
200 }
201
202 private boolean iterate(final String path, final int startIncl,
203 final int endExcl, boolean assumeDirectory, boolean pathMatch) {
204 int matcher = 0;
205 int right = startIncl;
206 boolean match = false;
207 int lastWildmatch = -1;
208
209
210
211
212 int wildmatchBacktrackPos = -1;
213 while (true) {
214 int left = right;
215 right = path.indexOf(slash, right);
216 if (right == -1) {
217 if (left < endExcl) {
218 match = matches(matcher, path, left, endExcl,
219 assumeDirectory, pathMatch);
220 } else {
221
222 match = match && !isWild(matchers.get(matcher));
223 }
224 if (match) {
225 if (matcher < matchers.size() - 1
226 && isWild(matchers.get(matcher))) {
227
228 matcher++;
229 match = matches(matcher, path, left, endExcl,
230 assumeDirectory, pathMatch);
231 } else if (dirOnly && !assumeDirectory) {
232
233 return false;
234 }
235 }
236 return match && matcher + 1 == matchers.size();
237 }
238 if (wildmatchBacktrackPos < 0) {
239 wildmatchBacktrackPos = right;
240 }
241 if (right - left > 0) {
242 match = matches(matcher, path, left, right, assumeDirectory,
243 pathMatch);
244 } else {
245
246 right++;
247 continue;
248 }
249 if (match) {
250 boolean wasWild = isWild(matchers.get(matcher));
251 if (wasWild) {
252 lastWildmatch = matcher;
253 wildmatchBacktrackPos = -1;
254
255 right = left - 1;
256 }
257 matcher++;
258 if (matcher == matchers.size()) {
259
260 if (!pathMatch) {
261 return true;
262 }
263 if (right == endExcl - 1) {
264
265
266 return !dirOnly || assumeDirectory;
267 }
268
269 if (wasWild) {
270 return true;
271 }
272 if (lastWildmatch >= 0) {
273
274
275
276 matcher = lastWildmatch + 1;
277 right = wildmatchBacktrackPos;
278 wildmatchBacktrackPos = -1;
279 } else {
280 return false;
281 }
282 }
283 } else if (lastWildmatch != -1) {
284 matcher = lastWildmatch + 1;
285 right = wildmatchBacktrackPos;
286 wildmatchBacktrackPos = -1;
287 } else {
288 return false;
289 }
290 right++;
291 }
292 }
293
294 private boolean matches(int matcherIdx, String path, int startIncl,
295 int endExcl, boolean assumeDirectory, boolean pathMatch) {
296 IMatcher matcher = matchers.get(matcherIdx);
297
298 final boolean matches = matcher.matches(path, startIncl, endExcl);
299 if (!matches || !pathMatch || matcherIdx < matchers.size() - 1
300 || !(matcher instanceof AbstractMatcher)) {
301 return matches;
302 }
303
304 return assumeDirectory || !((AbstractMatcher) matcher).dirOnly;
305 }
306
307 private static boolean isWild(IMatcher matcher) {
308 return matcher == WILD_NO_DIRECTORY || matcher == WILD_ONLY_DIRECTORY;
309 }
310 }