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