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 public synchronized Object remove(Object pathSpec)
330 {
331 if (pathSpec!=null)
332 {
333 String spec=(String) pathSpec;
334 if (spec.equals("/*"))
335 _prefixDefault=null;
336 else if (spec.endsWith("/*"))
337 {
338 _prefixMap.remove(spec.substring(0,spec.length()-2));
339 _exactMap.remove(spec.substring(0,spec.length()-1));
340 _exactMap.remove(spec.substring(0,spec.length()-2));
341 }
342 else if (spec.startsWith("*."))
343 _suffixMap.remove(spec.substring(2));
344 else if (spec.equals(URIUtil.SLASH))
345 {
346 _default=null;
347 _defaultSingletonList=null;
348 }
349 else
350 _exactMap.remove(spec);
351 }
352 return super.remove(pathSpec);
353 }
354
355
356 public void clear()
357 {
358 _exactMap=new StringMap();
359 _prefixMap=new StringMap();
360 _suffixMap=new StringMap();
361 _default=null;
362 _defaultSingletonList=null;
363 super.clear();
364 }
365
366
367
368
369
370 public static boolean match(String pathSpec, String path)
371 throws IllegalArgumentException
372 {
373 return match(pathSpec, path, false);
374 }
375
376
377
378
379
380 public static boolean match(String pathSpec, String path, boolean noDefault)
381 throws IllegalArgumentException
382 {
383 char c = pathSpec.charAt(0);
384 if (c=='/')
385 {
386 if (!noDefault && pathSpec.length()==1 || pathSpec.equals(path))
387 return true;
388
389 if(isPathWildcardMatch(pathSpec, path))
390 return true;
391 }
392 else if (c=='*')
393 return path.regionMatches(path.length()-pathSpec.length()+1,
394 pathSpec,1,pathSpec.length()-1);
395 return false;
396 }
397
398
399 private static boolean isPathWildcardMatch(String pathSpec, String path)
400 {
401
402 int cpl=pathSpec.length()-2;
403 if (pathSpec.endsWith("/*") && path.regionMatches(0,pathSpec,0,cpl))
404 {
405 if (path.length()==cpl || '/'==path.charAt(cpl))
406 return true;
407 }
408 return false;
409 }
410
411
412
413
414
415
416 public static String pathMatch(String pathSpec, String path)
417 {
418 char c = pathSpec.charAt(0);
419
420 if (c=='/')
421 {
422 if (pathSpec.length()==1)
423 return path;
424
425 if (pathSpec.equals(path))
426 return path;
427
428 if (isPathWildcardMatch(pathSpec, path))
429 return path.substring(0,pathSpec.length()-2);
430 }
431 else if (c=='*')
432 {
433 if (path.regionMatches(path.length()-(pathSpec.length()-1),
434 pathSpec,1,pathSpec.length()-1))
435 return path;
436 }
437 return null;
438 }
439
440
441
442
443
444 public static String pathInfo(String pathSpec, String path)
445 {
446 char c = pathSpec.charAt(0);
447
448 if (c=='/')
449 {
450 if (pathSpec.length()==1)
451 return null;
452
453 if (pathSpec.equals(path))
454 return null;
455
456 if (isPathWildcardMatch(pathSpec, path))
457 {
458 if (path.length()==pathSpec.length()-2)
459 return null;
460 return path.substring(pathSpec.length()-2);
461 }
462 }
463 return null;
464 }
465
466
467
468
469
470
471
472
473
474 public static String relativePath(String base,
475 String pathSpec,
476 String path )
477 {
478 String info=pathInfo(pathSpec,path);
479 if (info==null)
480 info=path;
481
482 if( info.startsWith( "./"))
483 info = info.substring( 2);
484 if( base.endsWith( URIUtil.SLASH))
485 if( info.startsWith( URIUtil.SLASH))
486 path = base + info.substring(1);
487 else
488 path = base + info;
489 else
490 if( info.startsWith( URIUtil.SLASH))
491 path = base + info;
492 else
493 path = base + URIUtil.SLASH + info;
494 return path;
495 }
496
497
498
499
500 public static class Entry implements Map.Entry
501 {
502 private Object key;
503 private Object value;
504 private String mapped;
505 private transient String string;
506
507 Entry(Object key, Object value)
508 {
509 this.key=key;
510 this.value=value;
511 }
512
513 public Object getKey()
514 {
515 return key;
516 }
517
518 public Object getValue()
519 {
520 return value;
521 }
522
523 public Object setValue(Object o)
524 {
525 throw new UnsupportedOperationException();
526 }
527
528 public String toString()
529 {
530 if (string==null)
531 string=key+"="+value;
532 return string;
533 }
534
535 public String getMapped()
536 {
537 return mapped;
538 }
539
540 void setMapped(String mapped)
541 {
542 this.mapped = mapped;
543 }
544 }
545 }