1 package com.atlassian.plugin.descriptors.servlet.util;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.concurrent.locks.ReentrantReadWriteLock;
12 import java.util.concurrent.locks.ReadWriteLock;
13
14
15
16
17
18 public class DefaultPathMapper implements Serializable, PathMapper
19 {
20 private static final String[] DEFAULT_KEYS = { "/", "*", "/*" };
21
22 private final Map<String,String> mappings = new HashMap<String,String>();
23 private final List<String> complexPaths = new ArrayList();
24
25 private final KeyMatcher matcher = new KeyMatcher();
26
27
28
29 private final ReadWriteLock lock = new ReentrantReadWriteLock();
30
31 public void put(final String key, final String pattern)
32 {
33 lock.writeLock().lock();
34 try
35 {
36 if (pattern == null)
37 {
38 removeMappingsForKey(key);
39 return;
40 }
41 mappings.put(pattern, key);
42 if ((pattern.indexOf('?') > -1) || ((pattern.indexOf("*") > -1) && (pattern.length() > 1)))
43 {
44 complexPaths.add(pattern);
45 }
46 }
47 finally
48 {
49 lock.writeLock().unlock();
50 }
51 }
52
53
54 private void removeMappingsForKey(final String key)
55 {
56 for (final Iterator it = mappings.entrySet().iterator(); it.hasNext();)
57 {
58 final Map.Entry entry = (Map.Entry) it.next();
59 if (entry.getValue().equals(key))
60 {
61 complexPaths.remove(entry.getKey());
62 it.remove();
63 }
64 }
65 }
66
67 public String get(String path)
68 {
69 lock.readLock().lock();
70 try
71 {
72 if (path == null)
73 {
74 path = "/";
75 }
76 final String mapped = matcher.findKey(path, mappings, complexPaths);
77 if (mapped == null)
78 {
79 return null;
80 }
81 return (String) mappings.get(mapped);
82 }
83 finally
84 {
85 lock.readLock().unlock();
86 }
87 }
88
89
90
91
92 public Collection getAll(String path)
93 {
94 lock.readLock().lock();
95 try
96 {
97 if (path == null)
98 {
99 path = "/";
100 }
101 final List<String> matches = new ArrayList();
102
103 final String exactKey = matcher.findExactKey(path, mappings);
104 if (exactKey != null)
105 {
106 matches.add(mappings.get(exactKey));
107 }
108
109 for (final Iterator iterator = matcher.findComplexKeys(path, complexPaths).iterator(); iterator.hasNext();)
110 {
111 final String mapped = (String) iterator.next();
112 matches.add(mappings.get(mapped));
113 }
114
115 for (final Iterator iterator = matcher.findDefaultKeys(mappings).iterator(); iterator.hasNext();)
116 {
117 final String mapped = (String) iterator.next();
118 matches.add(mappings.get(mapped));
119 }
120 return Collections.unmodifiableCollection(matches);
121 }
122 finally
123 {
124 lock.readLock().unlock();
125 }
126 }
127
128 public String toString()
129 {
130 final StringBuffer sb = new StringBuffer(30 * (mappings.size() + complexPaths.size()));
131 sb.append("Mappings:\n");
132 for (final Iterator iterator = mappings.keySet().iterator(); iterator.hasNext();)
133 {
134 final String key = (String) iterator.next();
135 sb.append(key).append("=").append(mappings.get(key)).append("\n");
136 }
137 sb.append("Complex Paths:\n");
138 for (final Iterator iterator = complexPaths.iterator(); iterator.hasNext();)
139 {
140 final String path = (String) iterator.next();
141 sb.append(path).append("\n");
142 }
143
144 return sb.toString();
145 }
146
147 private final class KeyMatcher
148 {
149
150 String findKey(final String path, final Map mappings, final List keys)
151 {
152 String result = findExactKey(path, mappings);
153 if (result == null)
154 {
155 result = findComplexKey(path, keys);
156 }
157 if (result == null)
158 {
159 result = findDefaultKey(mappings);
160 }
161 return result;
162 }
163
164
165 String findExactKey(final String path, final Map mappings)
166 {
167 if (mappings.containsKey(path))
168 {
169 return path;
170 }
171 return null;
172 }
173
174
175 String findComplexKey(final String path, final List complexPaths)
176 {
177 final int size = complexPaths.size();
178 for (int i = 0; i < size; i++)
179 {
180 final String key = (String) complexPaths.get(i);
181 if (match(key, path, false))
182 {
183 return key;
184 }
185 }
186 return null;
187 }
188
189
190 Collection findComplexKeys(final String path, final List complexPaths)
191 {
192 final int size = complexPaths.size();
193 final List matches = new ArrayList();
194 for (int i = 0; i < size; i++)
195 {
196 final String key = (String) complexPaths.get(i);
197 if (match(key, path, false))
198 {
199 matches.add(key);
200 }
201 }
202 return matches;
203 }
204
205
206 String findDefaultKey(final Map mappings)
207 {
208 for (int i = 0; i < DefaultPathMapper.DEFAULT_KEYS.length; i++)
209 {
210 if (mappings.containsKey(DefaultPathMapper.DEFAULT_KEYS[i]))
211 {
212 return DefaultPathMapper.DEFAULT_KEYS[i];
213 }
214 }
215 return null;
216 }
217
218
219 Collection findDefaultKeys(final Map mappings)
220 {
221 final List matches = new ArrayList();
222
223 for (int i = 0; i < DefaultPathMapper.DEFAULT_KEYS.length; i++)
224 {
225 if (mappings.containsKey(DefaultPathMapper.DEFAULT_KEYS[i]))
226 {
227 matches.add(DefaultPathMapper.DEFAULT_KEYS[i]);
228 }
229 }
230
231 return matches;
232 }
233
234 boolean match(final String pattern, final String str, final boolean isCaseSensitive)
235 {
236 final char[] patArr = pattern.toCharArray();
237 final char[] strArr = str.toCharArray();
238 int patIdxStart = 0;
239 int patIdxEnd = patArr.length - 1;
240 int strIdxStart = 0;
241 int strIdxEnd = strArr.length - 1;
242 char ch;
243
244 boolean containsStar = false;
245 for (int i = 0; i < patArr.length; i++)
246 {
247 if (patArr[i] == '*')
248 {
249 containsStar = true;
250 break;
251 }
252 }
253
254 if (!containsStar)
255 {
256
257 if (patIdxEnd != strIdxEnd)
258 {
259 return false;
260 }
261 for (int i = 0; i <= patIdxEnd; i++)
262 {
263 ch = patArr[i];
264 if (ch != '?')
265 {
266 if (isCaseSensitive && (ch != strArr[i]))
267 {
268 return false;
269 }
270 if (!isCaseSensitive && (Character.toUpperCase(ch) != Character.toUpperCase(strArr[i])))
271 {
272 return false;
273 }
274 }
275 }
276 return true;
277 }
278
279 if (patIdxEnd == 0)
280 {
281 return true;
282 }
283
284
285 while (((ch = patArr[patIdxStart]) != '*') && (strIdxStart <= strIdxEnd))
286 {
287 if (ch != '?')
288 {
289 if (isCaseSensitive && (ch != strArr[strIdxStart]))
290 {
291 return false;
292 }
293 if (!isCaseSensitive && (Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxStart])))
294 {
295 return false;
296 }
297 }
298 patIdxStart++;
299 strIdxStart++;
300 }
301 if (strIdxStart > strIdxEnd)
302 {
303
304
305 for (int i = patIdxStart; i <= patIdxEnd; i++)
306 {
307 if (patArr[i] != '*')
308 {
309 return false;
310 }
311 }
312 return true;
313 }
314
315
316 while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd))
317 {
318 if (ch != '?')
319 {
320 if (isCaseSensitive && (ch != strArr[strIdxEnd]))
321 {
322 return false;
323 }
324 if (!isCaseSensitive && (Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxEnd])))
325 {
326 return false;
327 }
328 }
329 patIdxEnd--;
330 strIdxEnd--;
331 }
332 if (strIdxStart > strIdxEnd)
333 {
334
335
336 for (int i = patIdxStart; i <= patIdxEnd; i++)
337 {
338 if (patArr[i] != '*')
339 {
340 return false;
341 }
342 }
343 return true;
344 }
345
346
347
348 while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd))
349 {
350 int patIdxTmp = -1;
351 for (int i = patIdxStart + 1; i <= patIdxEnd; i++)
352 {
353 if (patArr[i] == '*')
354 {
355 patIdxTmp = i;
356 break;
357 }
358 }
359 if (patIdxTmp == patIdxStart + 1)
360 {
361
362 patIdxStart++;
363 continue;
364 }
365
366
367 final int patLength = (patIdxTmp - patIdxStart - 1);
368 final int strLength = (strIdxEnd - strIdxStart + 1);
369 int foundIdx = -1;
370 strLoop : for (int i = 0; i <= strLength - patLength; i++)
371 {
372 for (int j = 0; j < patLength; j++)
373 {
374 ch = patArr[patIdxStart + j + 1];
375 if (ch != '?')
376 {
377 if (isCaseSensitive && (ch != strArr[strIdxStart + i + j]))
378 {
379 continue strLoop;
380 }
381 if (!isCaseSensitive && (Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxStart + i + j])))
382 {
383 continue strLoop;
384 }
385 }
386 }
387
388 foundIdx = strIdxStart + i;
389 break;
390 }
391
392 if (foundIdx == -1)
393 {
394 return false;
395 }
396
397 patIdxStart = patIdxTmp;
398 strIdxStart = foundIdx + patLength;
399 }
400
401
402
403 for (int i = patIdxStart; i <= patIdxEnd; i++)
404 {
405 if (patArr[i] != '*')
406 {
407 return false;
408 }
409 }
410 return true;
411 }
412 }
413 }