1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.servlet;
16
17 import java.io.Externalizable;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23
24 import org.mortbay.util.LazyList;
25 import org.mortbay.util.SingletonList;
26 import org.mortbay.util.StringMap;
27 import org.mortbay.util.URIUtil;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class PathMap extends HashMap implements Externalizable
61 {
62
63 private static String __pathSpecSeparators =
64 System.getProperty("org.mortbay.http.PathMap.separators",":,");
65
66
67
68
69
70
71
72
73
74 public static void setPathSpecSeparators(String s)
75 {
76 __pathSpecSeparators=s;
77 }
78
79
80 StringMap _prefixMap=new StringMap();
81 StringMap _suffixMap=new StringMap();
82 StringMap _exactMap=new StringMap();
83
84 List _defaultSingletonList=null;
85 Entry _prefixDefault=null;
86 Entry _default=null;
87 Set _entrySet;
88 boolean _nodefault=false;
89
90
91
92
93 public PathMap()
94 {
95 super(11);
96 _entrySet=entrySet();
97 }
98
99
100
101
102 public PathMap(boolean nodefault)
103 {
104 super(11);
105 _entrySet=entrySet();
106 _nodefault=nodefault;
107 }
108
109
110
111
112 public PathMap(int capacity)
113 {
114 super (capacity);
115 _entrySet=entrySet();
116 }
117
118
119
120
121 public PathMap(Map m)
122 {
123 putAll(m);
124 _entrySet=entrySet();
125 }
126
127
128 public void writeExternal(java.io.ObjectOutput out)
129 throws java.io.IOException
130 {
131 HashMap map = new HashMap(this);
132 out.writeObject(map);
133 }
134
135
136 public void readExternal(java.io.ObjectInput in)
137 throws java.io.IOException, ClassNotFoundException
138 {
139 HashMap map = (HashMap)in.readObject();
140 this.putAll(map);
141 }
142
143
144
145
146
147
148
149 public synchronized Object put(Object pathSpec, Object object)
150 {
151 StringTokenizer tok = new StringTokenizer(pathSpec.toString(),__pathSpecSeparators);
152 Object old =null;
153
154 while (tok.hasMoreTokens())
155 {
156 String spec=tok.nextToken();
157
158 if (!spec.startsWith("/") && !spec.startsWith("*."))
159 throw new IllegalArgumentException("PathSpec "+spec+". must start with '/' or '*.'");
160
161 old = super.put(spec,object);
162
163
164 Entry entry = new Entry(spec,object);
165
166 if (entry.getKey().equals(spec))
167 {
168 if (spec.equals("/*"))
169 _prefixDefault=entry;
170 else if (spec.endsWith("/*"))
171 {
172 String mapped=spec.substring(0,spec.length()-2);
173 entry.setMapped(mapped);
174 _prefixMap.put(mapped,entry);
175 _exactMap.put(mapped,entry);
176 _exactMap.put(spec.substring(0,spec.length()-1),entry);
177 }
178 else if (spec.startsWith("*."))
179 _suffixMap.put(spec.substring(2),entry);
180 else if (spec.equals(URIUtil.SLASH))
181 {
182 if (_nodefault)
183 _exactMap.put(spec,entry);
184 else
185 {
186 _default=entry;
187 _defaultSingletonList=
188 SingletonList.newSingletonList(_default);
189 }
190 }
191 else
192 {
193 entry.setMapped(spec);
194 _exactMap.put(spec,entry);
195 }
196 }
197 }
198
199 return old;
200 }
201
202
203
204
205
206
207 public Object match(String path)
208 {
209 Map.Entry entry = getMatch(path);
210 if (entry!=null)
211 return entry.getValue();
212 return null;
213 }
214
215
216
217
218
219
220
221 public Entry getMatch(String path)
222 {
223 Map.Entry entry;
224
225 if (path==null)
226 return null;
227
228 int l=path.length();
229
230
231 entry=_exactMap.getEntry(path,0,l);
232 if (entry!=null)
233 return (Entry) entry.getValue();
234
235
236 int i=l;
237 while((i=path.lastIndexOf('/',i-1))>=0)
238 {
239 entry=_prefixMap.getEntry(path,0,i);
240 if (entry!=null)
241 return (Entry) entry.getValue();
242 }
243
244
245 if (_prefixDefault!=null)
246 return _prefixDefault;
247
248
249 i=0;
250 while ((i=path.indexOf('.',i+1))>0)
251 {
252 entry=_suffixMap.getEntry(path,i+1,l-i-1);
253 if (entry!=null)
254 return (Entry) entry.getValue();
255 }
256
257
258 return _default;
259 }
260
261
262
263
264
265
266
267 public Object getLazyMatches(String path)
268 {
269 Map.Entry entry;
270 Object entries=null;
271
272 if (path==null)
273 return LazyList.getList(entries);
274
275 int l=path.length();
276
277
278 entry=_exactMap.getEntry(path,0,l);
279 if (entry!=null)
280 entries=LazyList.add(entries,entry.getValue());
281
282
283 int i=l-1;
284 while((i=path.lastIndexOf('/',i-1))>=0)
285 {
286 entry=_prefixMap.getEntry(path,0,i);
287 if (entry!=null)
288 entries=LazyList.add(entries,entry.getValue());
289 }
290
291
292 if (_prefixDefault!=null)
293 entries=LazyList.add(entries,_prefixDefault);
294
295
296 i=0;
297 while ((i=path.indexOf('.',i+1))>0)
298 {
299 entry=_suffixMap.getEntry(path,i+1,l-i-1);
300 if (entry!=null)
301 entries=LazyList.add(entries,entry.getValue());
302 }
303
304
305 if (_default!=null)
306 {
307
308 if (entries==null)
309 return _defaultSingletonList;
310
311 entries=LazyList.add(entries,_default);
312 }
313
314 return entries;
315 }
316
317
318
319
320
321
322
323 public List getMatches(String path)
324 {
325 return LazyList.getList(getLazyMatches(path));
326 }
327
328
329
330
331
332
333
334 public boolean containsMatch(String path)
335 {
336 Entry match = getMatch(path);
337 return match!=null && !match.equals(_default);
338 }
339
340
341
342
343 public synchronized Object remove(Object pathSpec)
344 {
345 if (pathSpec!=null)
346 {
347 String spec=(String) pathSpec;
348 if (spec.equals("/*"))
349 _prefixDefault=null;
350 else if (spec.endsWith("/*"))
351 {
352 _prefixMap.remove(spec.substring(0,spec.length()-2));
353 _exactMap.remove(spec.substring(0,spec.length()-1));
354 _exactMap.remove(spec.substring(0,spec.length()-2));
355 }
356 else if (spec.startsWith("*."))
357 _suffixMap.remove(spec.substring(2));
358 else if (spec.equals(URIUtil.SLASH))
359 {
360 _default=null;
361 _defaultSingletonList=null;
362 }
363 else
364 _exactMap.remove(spec);
365 }
366 return super.remove(pathSpec);
367 }
368
369
370 public void clear()
371 {
372 _exactMap=new StringMap();
373 _prefixMap=new StringMap();
374 _suffixMap=new StringMap();
375 _default=null;
376 _defaultSingletonList=null;
377 super.clear();
378 }
379
380
381
382
383
384 public static boolean match(String pathSpec, String path)
385 throws IllegalArgumentException
386 {
387 return match(pathSpec, path, false);
388 }
389
390
391
392
393
394 public static boolean match(String pathSpec, String path, boolean noDefault)
395 throws IllegalArgumentException
396 {
397 char c = pathSpec.charAt(0);
398 if (c=='/')
399 {
400 if (!noDefault && pathSpec.length()==1 || pathSpec.equals(path))
401 return true;
402
403 if(isPathWildcardMatch(pathSpec, path))
404 return true;
405 }
406 else if (c=='*')
407 return path.regionMatches(path.length()-pathSpec.length()+1,
408 pathSpec,1,pathSpec.length()-1);
409 return false;
410 }
411
412
413 private static boolean isPathWildcardMatch(String pathSpec, String path)
414 {
415
416 int cpl=pathSpec.length()-2;
417 if (pathSpec.endsWith("/*") && path.regionMatches(0,pathSpec,0,cpl))
418 {
419 if (path.length()==cpl || '/'==path.charAt(cpl))
420 return true;
421 }
422 return false;
423 }
424
425
426
427
428
429
430 public static String pathMatch(String pathSpec, String path)
431 {
432 char c = pathSpec.charAt(0);
433
434 if (c=='/')
435 {
436 if (pathSpec.length()==1)
437 return path;
438
439 if (pathSpec.equals(path))
440 return path;
441
442 if (isPathWildcardMatch(pathSpec, path))
443 return path.substring(0,pathSpec.length()-2);
444 }
445 else if (c=='*')
446 {
447 if (path.regionMatches(path.length()-(pathSpec.length()-1),
448 pathSpec,1,pathSpec.length()-1))
449 return path;
450 }
451 return null;
452 }
453
454
455
456
457
458 public static String pathInfo(String pathSpec, String path)
459 {
460 char c = pathSpec.charAt(0);
461
462 if (c=='/')
463 {
464 if (pathSpec.length()==1)
465 return null;
466
467 boolean wildcard = isPathWildcardMatch(pathSpec, path);
468
469
470 if (pathSpec.equals(path) && !wildcard)
471 return null;
472
473 if (wildcard)
474 {
475 if (path.length()==pathSpec.length()-2)
476 return null;
477 return path.substring(pathSpec.length()-2);
478 }
479 }
480 return null;
481 }
482
483
484
485
486
487
488
489
490
491 public static String relativePath(String base,
492 String pathSpec,
493 String path )
494 {
495 String info=pathInfo(pathSpec,path);
496 if (info==null)
497 info=path;
498
499 if( info.startsWith( "./"))
500 info = info.substring( 2);
501 if( base.endsWith( URIUtil.SLASH))
502 if( info.startsWith( URIUtil.SLASH))
503 path = base + info.substring(1);
504 else
505 path = base + info;
506 else
507 if( info.startsWith( URIUtil.SLASH))
508 path = base + info;
509 else
510 path = base + URIUtil.SLASH + info;
511 return path;
512 }
513
514
515
516
517 public static class Entry implements Map.Entry
518 {
519 private Object key;
520 private Object value;
521 private String mapped;
522 private transient String string;
523
524 Entry(Object key, Object value)
525 {
526 this.key=key;
527 this.value=value;
528 }
529
530 public Object getKey()
531 {
532 return key;
533 }
534
535 public Object getValue()
536 {
537 return value;
538 }
539
540 public Object setValue(Object o)
541 {
542 throw new UnsupportedOperationException();
543 }
544
545 public String toString()
546 {
547 if (string==null)
548 string=key+"="+value;
549 return string;
550 }
551
552 public String getMapped()
553 {
554 return mapped;
555 }
556
557 void setMapped(String mapped)
558 {
559 this.mapped = mapped;
560 }
561 }
562 }