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