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