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