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