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 = WildMatcher.INSTANCE;
65
66 private final List<IMatcher> matchers;
67
68 private final char slash;
69
70 private final boolean beginning;
71
72 private PathMatcher(String pattern, Character pathSeparator,
73 boolean dirOnly)
74 throws InvalidPatternException {
75 super(pattern, dirOnly);
76 slash = getPathSeparator(pathSeparator);
77 beginning = pattern.indexOf(slash) == 0;
78 if (isSimplePathWithSegments(pattern))
79 matchers = null;
80 else
81 matchers = createMatchers(split(pattern, slash), pathSeparator,
82 dirOnly);
83 }
84
85 private boolean isSimplePathWithSegments(String path) {
86 return !isWildCard(path) && path.indexOf('\\') < 0
87 && count(path, slash, true) > 0;
88 }
89
90 private static List<IMatcher> createMatchers(List<String> segments,
91 Character pathSeparator, boolean dirOnly)
92 throws InvalidPatternException {
93 List<IMatcher> matchers = new ArrayList<>(segments.size());
94 for (int i = 0; i < segments.size(); i++) {
95 String segment = segments.get(i);
96 IMatcher matcher = createNameMatcher0(segment, pathSeparator,
97 dirOnly);
98 if (matcher == WILD && i > 0
99 && matchers.get(matchers.size() - 1) == WILD)
100
101 continue;
102 matchers.add(matcher);
103 }
104 return matchers;
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 public static IMatcher createPathMatcher(String pattern,
121 Character pathSeparator, boolean dirOnly)
122 throws InvalidPatternException {
123 pattern = trim(pattern);
124 char slash = Strings.getPathSeparator(pathSeparator);
125
126 int slashIdx = pattern.indexOf(slash, 1);
127 if (slashIdx > 0 && slashIdx < pattern.length() - 1)
128 return new PathMatcher(pattern, pathSeparator, dirOnly);
129 return createNameMatcher0(pattern, pathSeparator, dirOnly);
130 }
131
132
133
134
135
136
137
138
139
140 private static String trim(String pattern) {
141 while (pattern.length() > 0
142 && pattern.charAt(pattern.length() - 1) == ' ') {
143 if (pattern.length() > 1
144 && pattern.charAt(pattern.length() - 2) == '\\') {
145
146
147 pattern = pattern.substring(0, pattern.length() - 2) + " ";
148 return pattern;
149 }
150 pattern = pattern.substring(0, pattern.length() - 1);
151 }
152 return pattern;
153 }
154
155 private static IMatcher createNameMatcher0(String segment,
156 Character pathSeparator, boolean dirOnly)
157 throws InvalidPatternException {
158
159 if (WildMatcher.WILDMATCH.equals(segment)
160 || WildMatcher.WILDMATCH2.equals(segment))
161 return WILD;
162
163 PatternState state = checkWildCards(segment);
164 switch (state) {
165 case LEADING_ASTERISK_ONLY:
166 return new LeadingAsteriskMatcher(segment, pathSeparator, dirOnly);
167 case TRAILING_ASTERISK_ONLY:
168 return new TrailingAsteriskMatcher(segment, pathSeparator, dirOnly);
169 case COMPLEX:
170 return new WildCardMatcher(segment, pathSeparator, dirOnly);
171 default:
172 return new NameMatcher(segment, pathSeparator, dirOnly, true);
173 }
174 }
175
176
177 @Override
178 public boolean matches(String path, boolean assumeDirectory,
179 boolean pathMatch) {
180 if (matchers == null) {
181 return simpleMatch(path, assumeDirectory, pathMatch);
182 }
183 return iterate(path, 0, path.length(), assumeDirectory, pathMatch);
184 }
185
186
187
188
189
190
191 private boolean simpleMatch(String path, boolean assumeDirectory,
192 boolean pathMatch) {
193 boolean hasSlash = path.indexOf(slash) == 0;
194 if (beginning && !hasSlash) {
195 path = slash + path;
196 }
197 if (!beginning && hasSlash) {
198 path = path.substring(1);
199 }
200 if (path.equals(pattern)) {
201
202 return !dirOnly || assumeDirectory;
203 }
204
205
206
207
208
209 String prefix = pattern + slash;
210 if (pathMatch) {
211 return path.equals(prefix) && (!dirOnly || assumeDirectory);
212 }
213 if (path.startsWith(prefix)) {
214 return true;
215 }
216 return false;
217 }
218
219
220 @Override
221 public boolean matches(String segment, int startIncl, int endExcl,
222 boolean assumeDirectory) {
223 throw new UnsupportedOperationException(
224 "Path matcher works only on entire paths");
225 }
226
227 private boolean iterate(final String path, final int startIncl,
228 final int endExcl, boolean assumeDirectory, boolean pathMatch) {
229 int matcher = 0;
230 int right = startIncl;
231 boolean match = false;
232 int lastWildmatch = -1;
233
234
235
236
237 int wildmatchBacktrackPos = -1;
238 while (true) {
239 int left = right;
240 right = path.indexOf(slash, right);
241 if (right == -1) {
242 if (left < endExcl) {
243 match = matches(matcher, path, left, endExcl,
244 assumeDirectory);
245 } else {
246
247 match = match && matchers.get(matcher) != WILD;
248 }
249 if (match) {
250 if (matcher < matchers.size() - 1
251 && matchers.get(matcher) == WILD) {
252
253 matcher++;
254 match = matches(matcher, path, left, endExcl,
255 assumeDirectory);
256 } else if (dirOnly && !assumeDirectory) {
257
258 return false;
259 }
260 }
261 return match && matcher + 1 == matchers.size();
262 }
263 if (wildmatchBacktrackPos < 0) {
264 wildmatchBacktrackPos = right;
265 }
266 if (right - left > 0) {
267 match = matches(matcher, path, left, right, assumeDirectory);
268 } else {
269
270 right++;
271 continue;
272 }
273 if (match) {
274 boolean wasWild = matchers.get(matcher) == WILD;
275 if (wasWild) {
276 lastWildmatch = matcher;
277 wildmatchBacktrackPos = -1;
278
279 right = left - 1;
280 }
281 matcher++;
282 if (matcher == matchers.size()) {
283
284 if (!pathMatch) {
285 return true;
286 } else {
287 if (right == endExcl - 1) {
288
289
290 return !dirOnly || assumeDirectory;
291 }
292
293 if (wasWild) {
294 return true;
295 }
296 if (lastWildmatch >= 0) {
297
298
299
300 matcher = lastWildmatch + 1;
301 right = wildmatchBacktrackPos;
302 wildmatchBacktrackPos = -1;
303 } else {
304 return false;
305 }
306 }
307 }
308 } else if (lastWildmatch != -1) {
309 matcher = lastWildmatch + 1;
310 right = wildmatchBacktrackPos;
311 wildmatchBacktrackPos = -1;
312 } else {
313 return false;
314 }
315 right++;
316 }
317 }
318
319 private boolean matches(int matcherIdx, String path, int startIncl,
320 int endExcl,
321 boolean assumeDirectory) {
322 IMatcher matcher = matchers.get(matcherIdx);
323 return matcher.matches(path, startIncl, endExcl, assumeDirectory);
324 }
325 }