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