1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.util;
18
19 import java.util.Comparator;
20 import java.util.LinkedHashMap;
21 import java.util.Map;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class AntPathMatcher implements PathMatcher {
49
50 private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
51
52
53 public static final String DEFAULT_PATH_SEPARATOR = "/";
54
55 private String pathSeparator = DEFAULT_PATH_SEPARATOR;
56
57 private final Map<String, AntPathStringMatcher> stringMatcherCache =
58 new ConcurrentHashMap<String, AntPathStringMatcher>();
59
60
61
62 public void setPathSeparator(String pathSeparator) {
63 this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
64 }
65
66
67 public boolean isPattern(String path) {
68 return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
69 }
70
71 public boolean match(String pattern, String path) {
72 return doMatch(pattern, path, true, null);
73 }
74
75 public boolean matchStart(String pattern, String path) {
76 return doMatch(pattern, path, false, null);
77 }
78
79
80
81
82
83
84
85
86
87
88 protected boolean doMatch(String pattern, String path, boolean fullMatch,
89 Map<String, String> uriTemplateVariables) {
90
91 if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
92 return false;
93 }
94
95 String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
96 String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
97
98 int pattIdxStart = 0;
99 int pattIdxEnd = pattDirs.length - 1;
100 int pathIdxStart = 0;
101 int pathIdxEnd = pathDirs.length - 1;
102
103
104 while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
105 String patDir = pattDirs[pattIdxStart];
106 if ("**".equals(patDir)) {
107 break;
108 }
109 if (!matchStrings(patDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
110 return false;
111 }
112 pattIdxStart++;
113 pathIdxStart++;
114 }
115
116 if (pathIdxStart > pathIdxEnd) {
117
118 if (pattIdxStart > pattIdxEnd) {
119 return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
120 !path.endsWith(this.pathSeparator));
121 }
122 if (!fullMatch) {
123 return true;
124 }
125 if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
126 return true;
127 }
128 for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
129 if (!pattDirs[i].equals("**")) {
130 return false;
131 }
132 }
133 return true;
134 }
135 else if (pattIdxStart > pattIdxEnd) {
136
137 return false;
138 }
139 else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
140
141 return true;
142 }
143
144
145 while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
146 String patDir = pattDirs[pattIdxEnd];
147 if (patDir.equals("**")) {
148 break;
149 }
150 if (!matchStrings(patDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
151 return false;
152 }
153 pattIdxEnd--;
154 pathIdxEnd--;
155 }
156 if (pathIdxStart > pathIdxEnd) {
157
158 for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
159 if (!pattDirs[i].equals("**")) {
160 return false;
161 }
162 }
163 return true;
164 }
165
166 while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
167 int patIdxTmp = -1;
168 for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
169 if (pattDirs[i].equals("**")) {
170 patIdxTmp = i;
171 break;
172 }
173 }
174 if (patIdxTmp == pattIdxStart + 1) {
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
223 AntPathStringMatcher matcher = this.stringMatcherCache.get(pattern);
224 if (matcher == null) {
225 matcher = new AntPathStringMatcher(pattern);
226 this.stringMatcherCache.put(pattern, matcher);
227 }
228 return matcher.matchStrings(str, uriTemplateVariables);
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 public String extractPathWithinPattern(String pattern, String path) {
245 String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
246 String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
247
248 StringBuilder builder = new StringBuilder();
249
250
251 int puts = 0;
252 for (int i = 0; i < patternParts.length; i++) {
253 String patternPart = patternParts[i];
254 if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {
255 if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {
256 builder.append(this.pathSeparator);
257 }
258 builder.append(pathParts[i]);
259 puts++;
260 }
261 }
262
263
264 for (int i = patternParts.length; i < pathParts.length; i++) {
265 if (puts > 0 || i > 0) {
266 builder.append(this.pathSeparator);
267 }
268 builder.append(pathParts[i]);
269 }
270
271 return builder.toString();
272 }
273
274 public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
275 Map<String, String> variables = new LinkedHashMap<String, String>();
276 boolean result = doMatch(pattern, path, true, variables);
277 Assert.state(result, "Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
278 return variables;
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 public String combine(String pattern1, String pattern2) {
301 if (!StringUtils.hasText(pattern1) && !StringUtils.hasText(pattern2)) {
302 return "";
303 }
304 else if (!StringUtils.hasText(pattern1)) {
305 return pattern2;
306 }
307 else if (!StringUtils.hasText(pattern2)) {
308 return pattern1;
309 }
310 else if (!pattern1.contains("{") && match(pattern1, pattern2)) {
311 return pattern2;
312 }
313 else if (pattern1.endsWith("/*")) {
314 if (pattern2.startsWith("/")) {
315
316
317
318
319
320
321
322
323
324
325 booking
326 return pattern1 + pattern2;
327 }
328 else {
329 booking
330 return pattern1 + "/" + pattern2;
331 }
332 }
333 else {
334 int dotPos1 = pattern1.indexOf('.');
335 if (dotPos1 == -1) {
336
337 if (pattern1.endsWith("/") || pattern2.startsWith("/")) {
338 return pattern1 + pattern2;
339 }
340 else {
341 return pattern1 + "/" + pattern2;
342 }
343 }
344 String fileName1 = pattern1.substring(0, dotPos1);
345 String extension1 = pattern1.substring(dotPos1);
346 String fileName2;
347 String extension2;
348 int dotPos2 = pattern2.indexOf('.');
349 if (dotPos2 != -1) {
350 fileName2 = pattern2.substring(0, dotPos2);
351 extension2 = pattern2.substring(dotPos2);
352 }
353 else {
354 fileName2 = pattern2;
355 extension2 = "";
356 }
357 String fileName = fileName1.endsWith("*") ? fileName2 : fileName1;
358 String extension = extension1.startsWith("*") ? extension2 : extension1;
359
360 return fileName + extension;
361 }
362 }
363
364
365
366
367
368
369
370
371
372
373
374
375
376 public Comparator<String> getPatternComparator(String path) {
377 return new AntPatternComparator(path);
378 }
379
380
381 private static class AntPatternComparator implements Comparator<String> {
382
383 private final String path;
384
385 private AntPatternComparator(String path) {
386 this.path = path;
387 }
388
389 public int compare(String pattern1, String pattern2) {
390 if (pattern1 == null && pattern2 == null) {
391 return 0;
392 }
393 else if (pattern1 == null) {
394 return 1;
395 }
396 else if (pattern2 == null) {
397 return -1;
398 }
399 boolean pattern1EqualsPath = pattern1.equals(path);
400 boolean pattern2EqualsPath = pattern2.equals(path);
401 if (pattern1EqualsPath && pattern2EqualsPath) {
402 return 0;
403 }
404 else if (pattern1EqualsPath) {
405 return -1;
406 }
407 else if (pattern2EqualsPath) {
408 return 1;
409 }
410 int wildCardCount1 = getWildCardCount(pattern1);
411 int wildCardCount2 = getWildCardCount(pattern2);
412
413 int bracketCount1 = StringUtils.countOccurrencesOf(pattern1, "{");
414 int bracketCount2 = StringUtils.countOccurrencesOf(pattern2, "{");
415
416 int totalCount1 = wildCardCount1 + bracketCount1;
417 int totalCount2 = wildCardCount2 + bracketCount2;
418
419 if (totalCount1 != totalCount2) {
420 return totalCount1 - totalCount2;
421 }
422
423 int pattern1Length = getPatternLength(pattern1);
424 int pattern2Length = getPatternLength(pattern2);
425
426 if (pattern1Length != pattern2Length) {
427 return pattern2Length - pattern1Length;
428 }
429
430 if (wildCardCount1 < wildCardCount2) {
431 return -1;
432 }
433 else if (wildCardCount2 < wildCardCount1) {
434 return 1;
435 }
436
437 if (bracketCount1 < bracketCount2) {
438 return -1;
439 }
440 else if (bracketCount2 < bracketCount1) {
441 return 1;
442 }
443
444 return 0;
445 }
446
447 private int getWildCardCount(String pattern) {
448 if (pattern.endsWith(".*")) {
449 pattern = pattern.substring(0, pattern.length() - 2);
450 }
451 return StringUtils.countOccurrencesOf(pattern, "*");
452 }
453
454
455
456
457 private int getPatternLength(String pattern) {
458 Matcher m = VARIABLE_PATTERN.matcher(pattern);
459 return m.replaceAll("#").length();
460 }
461 }
462
463 }