KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > util > AntPathMatcher


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.util;
18
19 /**
20  * PathMatcher implementation for Ant-style path patterns.
21  * Examples are provided below.
22  *
23  * <p>Part of this mapping code has been kindly borrowed from
24  * <a HREF="http://ant.apache.org">Apache Ant</a>.
25  *
26  * <p>The mapping matches URLs using the following rules:<br>
27  * <ul>
28  * <li>? matches one character</li>
29  * <li>* matches zero or more characters</li>
30  * <li>** matches zero or more 'directories' in a path</li>
31  * </ul>
32  *
33  * <p>Some examples:<br>
34  * <ul>
35  * <li><code>com/t?st.jsp</code> - matches <code>com/test.jsp</code> but also
36  * <code>com/tast.jsp</code> or <code>com/txst.jsp</code></li>
37  * <li><code>com/*.jsp</code> - matches all <code>.jsp</code> files in the
38  * <code>com</code> directory</li>
39  * <li><code>com/&#42;&#42;/test.jsp</code> - matches all <code>test.jsp</code>
40  * files underneath the <code>com</code> path</li>
41  * <li><code>org/springframework/&#42;&#42;/*.jsp</code> - matches all <code>.jsp</code>
42  * files underneath the <code>org/springframework</code> path</li>
43  * <li><code>org/&#42;&#42;/servlet/bla.jsp</code> - matches
44  * <code>org/springframework/servlet/bla.jsp</code> but also
45  * <code>org/springframework/testing/servlet/bla.jsp</code> and
46  * <code>org/servlet/bla.jsp</code></li>
47  * </ul>
48  *
49  * @author Alef Arendsen
50  * @author Juergen Hoeller
51  * @author Rob Harrop
52  * @since 16.07.2003
53  */

54 public class AntPathMatcher implements PathMatcher {
55
56     /** Default path separator: "/" */
57     public static final String JavaDoc DEFAULT_PATH_SEPARATOR = "/";
58
59     private String JavaDoc pathSeparator = DEFAULT_PATH_SEPARATOR;
60
61
62     /**
63      * Set the path separator to use for pattern parsing.
64      * Default is "/", as in Ant.
65      */

66     public void setPathSeparator(String JavaDoc pathSeparator) {
67         this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
68     }
69
70
71     public boolean isPattern(String JavaDoc str) {
72         return (str.indexOf('*') != -1 || str.indexOf('?') != -1);
73     }
74
75     public boolean match(String JavaDoc pattern, String JavaDoc str) {
76         if (str.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
77             return false;
78         }
79
80         String JavaDoc[] patDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
81         String JavaDoc[] strDirs = StringUtils.tokenizeToStringArray(str, this.pathSeparator);
82
83         int patIdxStart = 0;
84         int patIdxEnd = patDirs.length - 1;
85         int strIdxStart = 0;
86         int strIdxEnd = strDirs.length - 1;
87
88         // Match all elements up to the first **
89
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
90             String JavaDoc patDir = (String JavaDoc) patDirs[patIdxStart];
91             if (patDir.equals("**")) {
92                 break;
93             }
94             if (!matchStrings(patDir, (String JavaDoc) strDirs[strIdxStart])) {
95                 return false;
96             }
97             patIdxStart++;
98             strIdxStart++;
99         }
100
101         if (strIdxStart > strIdxEnd) {
102             // String is exhausted, only match if rest of pattern is * or **'s
103
if (patIdxStart == patIdxEnd && patDirs[patIdxStart].equals("*") &&
104                     str.endsWith(this.pathSeparator)) {
105                 return true;
106             }
107             for (int i = patIdxStart; i <= patIdxEnd; i++) {
108                 if (!patDirs[i].equals("**")) {
109                     return false;
110                 }
111             }
112             return true;
113         }
114         else {
115             if (patIdxStart > patIdxEnd) {
116                 // String not exhausted, but pattern is. Failure.
117
return false;
118             }
119         }
120
121         // up to last '**'
122
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
123             String JavaDoc patDir = (String JavaDoc) patDirs[patIdxEnd];
124             if (patDir.equals("**")) {
125                 break;
126             }
127             if (!matchStrings(patDir, (String JavaDoc) strDirs[strIdxEnd])) {
128                 return false;
129             }
130             patIdxEnd--;
131             strIdxEnd--;
132         }
133         if (strIdxStart > strIdxEnd) {
134             // String is exhausted
135
for (int i = patIdxStart; i <= patIdxEnd; i++) {
136                 if (!patDirs[i].equals("**")) {
137                     return false;
138                 }
139             }
140             return true;
141         }
142
143         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
144             int patIdxTmp = -1;
145             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
146                 if (patDirs[i].equals("**")) {
147                     patIdxTmp = i;
148                     break;
149                 }
150             }
151             if (patIdxTmp == patIdxStart + 1) {
152                 // '**/**' situation, so skip one
153
patIdxStart++;
154                 continue;
155             }
156             // Find the pattern between padIdxStart & padIdxTmp in str between
157
// strIdxStart & strIdxEnd
158
int patLength = (patIdxTmp - patIdxStart - 1);
159             int strLength = (strIdxEnd - strIdxStart + 1);
160             int foundIdx = -1;
161             strLoop:
162                 for (int i = 0; i <= strLength - patLength; i++) {
163                     for (int j = 0; j < patLength; j++) {
164                         String JavaDoc subPat = (String JavaDoc) patDirs[patIdxStart + j + 1];
165                         String JavaDoc subStr = (String JavaDoc) strDirs[strIdxStart + i + j];
166                         if (!matchStrings(subPat, subStr)) {
167                             continue strLoop;
168                         }
169                     }
170
171                     foundIdx = strIdxStart + i;
172                     break;
173                 }
174
175             if (foundIdx == -1) {
176                 return false;
177             }
178
179             patIdxStart = patIdxTmp;
180             strIdxStart = foundIdx + patLength;
181         }
182
183         for (int i = patIdxStart; i <= patIdxEnd; i++) {
184             if (!patDirs[i].equals("**")) {
185                 return false;
186             }
187         }
188
189         return true;
190     }
191
192     /**
193      * Tests whether or not a string matches against a pattern.
194      * The pattern may contain two special characters:<br>
195      * '*' means zero or more characters<br>
196      * '?' means one and only one character
197      * @param pattern pattern to match against.
198      * Must not be <code>null</code>.
199      * @param str string which must be matched against the pattern.
200      * Must not be <code>null</code>.
201      * @return <code>true</code> if the string matches against the
202      * pattern, or <code>false</code> otherwise.
203      */

204     private boolean matchStrings(String JavaDoc pattern, String JavaDoc str) {
205         char[] patArr = pattern.toCharArray();
206         char[] strArr = str.toCharArray();
207         int patIdxStart = 0;
208         int patIdxEnd = patArr.length - 1;
209         int strIdxStart = 0;
210         int strIdxEnd = strArr.length - 1;
211         char ch;
212
213         boolean containsStar = false;
214         for (int i = 0; i < patArr.length; i++) {
215             if (patArr[i] == '*') {
216                 containsStar = true;
217                 break;
218             }
219         }
220
221         if (!containsStar) {
222             // No '*'s, so we make a shortcut
223
if (patIdxEnd != strIdxEnd) {
224                 return false; // Pattern and string do not have the same size
225
}
226             for (int i = 0; i <= patIdxEnd; i++) {
227                 ch = patArr[i];
228                 if (ch != '?') {
229                     if (ch != strArr[i]) {
230                         return false;// Character mismatch
231
}
232                 }
233             }
234             return true; // String matches against pattern
235
}
236
237
238         if (patIdxEnd == 0) {
239             return true; // Pattern contains only '*', which matches anything
240
}
241
242         // Process characters before first star
243
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
244             if (ch != '?') {
245                 if (ch != strArr[strIdxStart]) {
246                     return false;// Character mismatch
247
}
248             }
249             patIdxStart++;
250             strIdxStart++;
251         }
252         if (strIdxStart > strIdxEnd) {
253             // All characters in the string are used. Check if only '*'s are
254
// left in the pattern. If so, we succeeded. Otherwise failure.
255
for (int i = patIdxStart; i <= patIdxEnd; i++) {
256                 if (patArr[i] != '*') {
257                     return false;
258                 }
259             }
260             return true;
261         }
262
263         // Process characters after last star
264
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
265             if (ch != '?') {
266                 if (ch != strArr[strIdxEnd]) {
267                     return false;// Character mismatch
268
}
269             }
270             patIdxEnd--;
271             strIdxEnd--;
272         }
273         if (strIdxStart > strIdxEnd) {
274             // All characters in the string are used. Check if only '*'s are
275
// left in the pattern. If so, we succeeded. Otherwise failure.
276
for (int i = patIdxStart; i <= patIdxEnd; i++) {
277                 if (patArr[i] != '*') {
278                     return false;
279                 }
280             }
281             return true;
282         }
283
284         // process pattern between stars. padIdxStart and patIdxEnd point
285
// always to a '*'.
286
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
287             int patIdxTmp = -1;
288             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
289                 if (patArr[i] == '*') {
290                     patIdxTmp = i;
291                     break;
292                 }
293             }
294             if (patIdxTmp == patIdxStart + 1) {
295                 // Two stars next to each other, skip the first one.
296
patIdxStart++;
297                 continue;
298             }
299             // Find the pattern between padIdxStart & padIdxTmp in str between
300
// strIdxStart & strIdxEnd
301
int patLength = (patIdxTmp - patIdxStart - 1);
302             int strLength = (strIdxEnd - strIdxStart + 1);
303             int foundIdx = -1;
304             strLoop:
305             for (int i = 0; i <= strLength - patLength; i++) {
306                 for (int j = 0; j < patLength; j++) {
307                     ch = patArr[patIdxStart + j + 1];
308                     if (ch != '?') {
309                         if (ch != strArr[strIdxStart + i + j]) {
310                             continue strLoop;
311                         }
312                     }
313                 }
314
315                 foundIdx = strIdxStart + i;
316                 break;
317             }
318
319             if (foundIdx == -1) {
320                 return false;
321             }
322
323             patIdxStart = patIdxTmp;
324             strIdxStart = foundIdx + patLength;
325         }
326
327         // All characters in the string are used. Check if only '*'s are left
328
// in the pattern. If so, we succeeded. Otherwise failure.
329
for (int i = patIdxStart; i <= patIdxEnd; i++) {
330             if (patArr[i] != '*') {
331                 return false;
332             }
333         }
334
335         return true;
336     }
337
338     /**
339      * Given a pattern and a full path, returns the non-pattern mapped part. E.g.:
340      * <ul>
341      * <li>'<code>/docs/*</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>
342      * <li>'<code>/docs/cvs/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>commit.html</code>'</li>
343      * <li>'<code>/docs/**</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>
344      * <li>'<code>/docs/**\/*.html</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit.html</code>'</li>
345      * </ul>
346      * <p>Assumes that {@link #match} returns <code>true</code> for '<code>pattern</code>'
347      * and '<code>path</code>', but does <strong>not</strong> enforce this.
348      */

349     public String JavaDoc extractPathWithinPattern(String JavaDoc pattern, String JavaDoc path) {
350         String JavaDoc[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
351         String JavaDoc[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
352
353         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
354
355         // Add any path parts that have a wildcarded pattern part.
356
int puts = 0;
357         for (int i = 0; i < patternParts.length; i++) {
358             String JavaDoc patternPart = patternParts[i];
359             if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {
360                 if (puts > 0) {
361                     buffer.append(this.pathSeparator);
362                 }
363                 buffer.append(pathParts[i]);
364                 puts++;
365             }
366         }
367
368         // Append any trailing path parts.
369
for (int i = patternParts.length; i < pathParts.length; i++) {
370             if (puts > 0 || i > 0) {
371                 buffer.append(this.pathSeparator);
372             }
373             buffer.append(pathParts[i]);
374         }
375
376         return buffer.toString();
377     }
378
379 }
380
Popular Tags