KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > module > sitemesh > mapper > PathMapper


1 /*
2  * Title: PathMapper
3  * Description:
4  *
5  * This software is published under the terms of the OpenSymphony Software
6  * License version 1.1, of which a copy has been included with this
7  * distribution in the LICENSE.txt file.
8  */

9
10 package com.opensymphony.module.sitemesh.mapper;
11
12 import java.util.HashMap JavaDoc;
13 import java.util.Map JavaDoc;
14 import java.util.Iterator JavaDoc;
15
16 /**
17  * The PathMapper is used to map file patterns to keys, and find an approriate
18  * key for a given file path. The pattern rules are consistent with those defined
19  * in the Servlet 2.3 API on the whole. Wildcard patterns are also supported, using
20  * any combination of * and ?.
21  *
22  * <h3>Example</h3>
23  *
24  * <blockquote><code>
25  * PathMapper pm = new PathMapper();<br>
26  * <br>
27  * pm.put("one","/");<br>
28  * pm.put("two","/mydir/*");<br>
29  * pm.put("three","*.xml");<br>
30  * pm.put("four","/myexactfile.html");<br>
31  * pm.put("five","/*\/admin/*.??ml");<br>
32  * <br>
33  * String result1 = pm.get("/mydir/myfile.xml"); // returns "two";<br>
34  * String result2 = pm.get("/mydir/otherdir/admin/myfile.html"); // returns "five";<br>
35  * </code></blockquote>
36  *
37  * @author <a HREF="mailto:joe@truemesh.com">Joe Walnes</a>
38  * @author <a HREF="mailto:mcannon@internet.com">Mike Cannon-Brookes</a>
39  * @author <a HREF="mailto:hani@formicary.net">Hani Suleiman</a>
40  * @version $Revision: 1.2 $
41  */

42 public final class PathMapper {
43     private Map JavaDoc mappings = new HashMap JavaDoc();
44
45     /** Add a key and appropriate matching pattern. */
46     public void put(String JavaDoc key, String JavaDoc pattern) {
47         if (key != null) {
48             mappings.put(pattern, key);
49         }
50     }
51
52     /** Retrieve appropriate key by matching patterns with supplied path. */
53     public String JavaDoc get(String JavaDoc path) {
54         if (path == null) path = "/";
55         String JavaDoc mapped = findKey(path, mappings);
56         if (mapped == null) return null;
57         return (String JavaDoc) mappings.get(mapped);
58     }
59
60     /** Find exact key in mappings. */
61     private static String JavaDoc findKey(String JavaDoc path, Map JavaDoc mappings) {
62         String JavaDoc result = findExactKey(path, mappings);
63         if (result == null) result = findComplexKey(path, mappings);
64         if (result == null) result = findDefaultKey(mappings);
65         return result;
66     }
67
68     /** Check if path matches exact pattern ( /blah/blah.jsp ). */
69     private static String JavaDoc findExactKey(String JavaDoc path, Map JavaDoc mappings) {
70         if (mappings.containsKey(path)) return path;
71         return null;
72     }
73
74     private static String JavaDoc findComplexKey(String JavaDoc path, Map JavaDoc mappings) {
75         Iterator JavaDoc i = mappings.keySet().iterator();
76         String JavaDoc result = null, key = null;
77         while (i.hasNext()) {
78             key = (String JavaDoc) i.next();
79             if (key.length() > 1 && (key.indexOf('?') != -1 || key.indexOf('*') != -1) && match(key, path, false)) {
80                 if (result == null || key.length() > result.length()) {
81                     // longest key wins
82
result = key;
83                 }
84             }
85         }
86         return result;
87     }
88
89     /** Look for root pattern ( / ). */
90     private static String JavaDoc findDefaultKey(Map JavaDoc mappings) {
91         String JavaDoc[] defaultKeys = {"/", "*", "/*"};
92         for (int i = 0; i < defaultKeys.length; i++) {
93             if (mappings.containsKey(defaultKeys[i])) return defaultKeys[i];
94         }
95         return null;
96     }
97
98     private static boolean match(String JavaDoc pattern, String JavaDoc str, boolean isCaseSensitive) {
99         char[] patArr = pattern.toCharArray();
100         char[] strArr = str.toCharArray();
101         int patIdxStart = 0;
102         int patIdxEnd = patArr.length - 1;
103         int strIdxStart = 0;
104         int strIdxEnd = strArr.length - 1;
105         char ch;
106
107         boolean containsStar = false;
108         for (int i = 0; i < patArr.length; i++) {
109             if (patArr[i] == '*') {
110                 containsStar = true;
111                 break;
112             }
113         }
114
115         if (!containsStar) {
116             // No '*'s, so we make a shortcut
117
if (patIdxEnd != strIdxEnd) {
118                 return false; // Pattern and string do not have the same size
119
}
120             for (int i = 0; i <= patIdxEnd; i++) {
121                 ch = patArr[i];
122                 if (ch != '?') {
123                     if (isCaseSensitive && ch != strArr[i]) {
124                         return false; // Character mismatch
125
}
126                     if (!isCaseSensitive && Character.toUpperCase(ch) !=
127                             Character.toUpperCase(strArr[i])) {
128                         return false; // Character mismatch
129
}
130                 }
131             }
132             return true; // String matches against pattern
133
}
134
135         if (patIdxEnd == 0) {
136             return true; // Pattern contains only '*', which matches anything
137
}
138
139         // Process characters before first star
140
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
141             if (ch != '?') {
142                 if (isCaseSensitive && ch != strArr[strIdxStart]) {
143                     return false; // Character mismatch
144
}
145                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
146                         Character.toUpperCase(strArr[strIdxStart])) {
147                     return false; // Character mismatch
148
}
149             }
150             patIdxStart++;
151             strIdxStart++;
152         }
153         if (strIdxStart > strIdxEnd) {
154             // All characters in the string are used. Check if only '*'s are
155
// left in the pattern. If so, we succeeded. Otherwise failure.
156
for (int i = patIdxStart; i <= patIdxEnd; i++) {
157                 if (patArr[i] != '*') {
158                     return false;
159                 }
160             }
161             return true;
162         }
163
164         // Process characters after last star
165
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
166             if (ch != '?') {
167                 if (isCaseSensitive && ch != strArr[strIdxEnd]) {
168                     return false; // Character mismatch
169
}
170                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
171                         Character.toUpperCase(strArr[strIdxEnd])) {
172                     return false; // Character mismatch
173
}
174             }
175             patIdxEnd--;
176             strIdxEnd--;
177         }
178         if (strIdxStart > strIdxEnd) {
179             // All characters in the string are used. Check if only '*'s are
180
// left in the pattern. If so, we succeeded. Otherwise failure.
181
for (int i = patIdxStart; i <= patIdxEnd; i++) {
182                 if (patArr[i] != '*') {
183                     return false;
184                 }
185             }
186             return true;
187         }
188
189         // process pattern between stars. padIdxStart and patIdxEnd point
190
// always to a '*'.
191
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
192             int patIdxTmp = -1;
193             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
194                 if (patArr[i] == '*') {
195                     patIdxTmp = i;
196                     break;
197                 }
198             }
199             if (patIdxTmp == patIdxStart + 1) {
200                 // Two stars next to each other, skip the first one.
201
patIdxStart++;
202                 continue;
203             }
204             // Find the pattern between padIdxStart & padIdxTmp in str between
205
// strIdxStart & strIdxEnd
206
int patLength = (patIdxTmp - patIdxStart - 1);
207             int strLength = (strIdxEnd - strIdxStart + 1);
208             int foundIdx = -1;
209             strLoop:
210             for (int i = 0; i <= strLength - patLength; i++) {
211                 for (int j = 0; j < patLength; j++) {
212                     ch = patArr[patIdxStart + j + 1];
213                     if (ch != '?') {
214                         if (isCaseSensitive && ch != strArr[strIdxStart + i + j]) {
215                             continue strLoop;
216                         }
217                         if (!isCaseSensitive && Character.toUpperCase(ch) !=
218                                 Character.toUpperCase(strArr[strIdxStart + i + j])) {
219                             continue strLoop;
220                         }
221                     }
222                 }
223
224                 foundIdx = strIdxStart + i;
225                 break;
226             }
227
228             if (foundIdx == -1) {
229                 return false;
230             }
231
232             patIdxStart = patIdxTmp;
233             strIdxStart = foundIdx + patLength;
234         }
235
236         // All characters in the string are used. Check if only '*'s are left
237
// in the pattern. If so, we succeeded. Otherwise failure.
238
for (int i = patIdxStart; i <= patIdxEnd; i++) {
239             if (patArr[i] != '*') {
240                 return false;
241             }
242         }
243         return true;
244     }
245 }
Popular Tags