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.Collections;
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.eclipse.jetty.util.LazyList;
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 @Override
147 public Object put(Object pathSpec, Object object)
148 {
149 String str = pathSpec.toString();
150 if ("".equals(str.trim()))
151 {
152 Entry entry = new Entry("",object);
153 entry.setMapped("");
154 _exactMap.put("", entry);
155 return super.put("", object);
156 }
157
158 StringTokenizer tok = new StringTokenizer(str,__pathSpecSeparators);
159 Object old =null;
160
161 while (tok.hasMoreTokens())
162 {
163 String spec=tok.nextToken();
164
165 if (!spec.startsWith("/") && !spec.startsWith("*."))
166 throw new IllegalArgumentException("PathSpec "+spec+". must start with '/' or '*.'");
167
168 old = super.put(spec,object);
169
170
171 Entry entry = new Entry(spec,object);
172
173 if (entry.getKey().equals(spec))
174 {
175 if (spec.equals("/*"))
176 _prefixDefault=entry;
177 else if (spec.endsWith("/*"))
178 {
179 String mapped=spec.substring(0,spec.length()-2);
180 entry.setMapped(mapped);
181 _prefixMap.put(mapped,entry);
182 _exactMap.put(mapped,entry);
183 _exactMap.put(spec.substring(0,spec.length()-1),entry);
184 }
185 else if (spec.startsWith("*."))
186 _suffixMap.put(spec.substring(2),entry);
187 else if (spec.equals(URIUtil.SLASH))
188 {
189 if (_nodefault)
190 _exactMap.put(spec,entry);
191 else
192 {
193 _default=entry;
194 _defaultSingletonList=
195 Collections.singletonList(_default);
196 }
197 }
198 else
199 {
200 entry.setMapped(spec);
201 _exactMap.put(spec,entry);
202 }
203 }
204 }
205
206 return old;
207 }
208
209
210
211
212
213
214 public Object match(String path)
215 {
216 Map.Entry entry = getMatch(path);
217 if (entry!=null)
218 return entry.getValue();
219 return null;
220 }
221
222
223
224
225
226
227
228 public Entry getMatch(String path)
229 {
230 Map.Entry entry=null;
231
232 if (path==null)
233 return null;
234
235 int l=path.length();
236
237
238 if (l == 1 && path.charAt(0)=='/')
239 {
240 entry = (Map.Entry)_exactMap.get("");
241 if (entry != null)
242 return (Entry)entry;
243 }
244
245
246 entry=_exactMap.getEntry(path,0,l);
247 if (entry!=null)
248 return (Entry) entry.getValue();
249
250
251 int i=l;
252 while((i=path.lastIndexOf('/',i-1))>=0)
253 {
254 entry=_prefixMap.getEntry(path,0,i);
255 if (entry!=null)
256 return (Entry) entry.getValue();
257 }
258
259
260 if (_prefixDefault!=null)
261 return _prefixDefault;
262
263
264 i=0;
265 while ((i=path.indexOf('.',i+1))>0)
266 {
267 entry=_suffixMap.getEntry(path,i+1,l-i-1);
268 if (entry!=null)
269 return (Entry) entry.getValue();
270 }
271
272
273 return _default;
274 }
275
276
277
278
279
280
281
282 public Object getLazyMatches(String path)
283 {
284 Map.Entry entry;
285 Object entries=null;
286
287 if (path==null)
288 return LazyList.getList(entries);
289
290 int l=path.length();
291
292
293 entry=_exactMap.getEntry(path,0,l);
294 if (entry!=null)
295 entries=LazyList.add(entries,entry.getValue());
296
297
298 int i=l-1;
299 while((i=path.lastIndexOf('/',i-1))>=0)
300 {
301 entry=_prefixMap.getEntry(path,0,i);
302 if (entry!=null)
303 entries=LazyList.add(entries,entry.getValue());
304 }
305
306
307 if (_prefixDefault!=null)
308 entries=LazyList.add(entries,_prefixDefault);
309
310
311 i=0;
312 while ((i=path.indexOf('.',i+1))>0)
313 {
314 entry=_suffixMap.getEntry(path,i+1,l-i-1);
315 if (entry!=null)
316 entries=LazyList.add(entries,entry.getValue());
317 }
318
319
320 if (_default!=null)
321 {
322
323 if (entries==null)
324 return _defaultSingletonList;
325
326 entries=LazyList.add(entries,_default);
327 }
328
329 return entries;
330 }
331
332
333
334
335
336
337
338 public List getMatches(String path)
339 {
340 return LazyList.getList(getLazyMatches(path));
341 }
342
343
344
345
346
347
348
349 public boolean containsMatch(String path)
350 {
351 Entry match = getMatch(path);
352 return match!=null && !match.equals(_default);
353 }
354
355
356 @Override
357 public Object remove(Object pathSpec)
358 {
359 if (pathSpec!=null)
360 {
361 String spec=(String) pathSpec;
362 if (spec.equals("/*"))
363 _prefixDefault=null;
364 else if (spec.endsWith("/*"))
365 {
366 _prefixMap.remove(spec.substring(0,spec.length()-2));
367 _exactMap.remove(spec.substring(0,spec.length()-1));
368 _exactMap.remove(spec.substring(0,spec.length()-2));
369 }
370 else if (spec.startsWith("*."))
371 _suffixMap.remove(spec.substring(2));
372 else if (spec.equals(URIUtil.SLASH))
373 {
374 _default=null;
375 _defaultSingletonList=null;
376 }
377 else
378 _exactMap.remove(spec);
379 }
380 return super.remove(pathSpec);
381 }
382
383
384 @Override
385 public void clear()
386 {
387 _exactMap.clear();
388 _prefixMap.clear();
389 _suffixMap.clear();
390 _default=null;
391 _defaultSingletonList=null;
392 super.clear();
393 }
394
395
396
397
398
399 public static boolean match(String pathSpec, String path)
400 throws IllegalArgumentException
401 {
402 return match(pathSpec, path, false);
403 }
404
405
406
407
408
409 public static boolean match(String pathSpec, String path, boolean noDefault)
410 throws IllegalArgumentException
411 {
412 char c = pathSpec.charAt(0);
413 if (c=='/')
414 {
415 if (!noDefault && pathSpec.length()==1 || pathSpec.equals(path))
416 return true;
417
418 if(isPathWildcardMatch(pathSpec, path))
419 return true;
420 }
421 else if (c=='*')
422 return path.regionMatches(path.length()-pathSpec.length()+1,
423 pathSpec,1,pathSpec.length()-1);
424 return false;
425 }
426
427
428 private static boolean isPathWildcardMatch(String pathSpec, String path)
429 {
430
431 int cpl=pathSpec.length()-2;
432 if (pathSpec.endsWith("/*") && path.regionMatches(0,pathSpec,0,cpl))
433 {
434 if (path.length()==cpl || '/'==path.charAt(cpl))
435 return true;
436 }
437 return false;
438 }
439
440
441
442
443
444
445 public static String pathMatch(String pathSpec, String path)
446 {
447 char c = pathSpec.charAt(0);
448
449 if (c=='/')
450 {
451 if (pathSpec.length()==1)
452 return path;
453
454 if (pathSpec.equals(path))
455 return path;
456
457 if (isPathWildcardMatch(pathSpec, path))
458 return path.substring(0,pathSpec.length()-2);
459 }
460 else if (c=='*')
461 {
462 if (path.regionMatches(path.length()-(pathSpec.length()-1),
463 pathSpec,1,pathSpec.length()-1))
464 return path;
465 }
466 return null;
467 }
468
469
470
471
472
473 public static String pathInfo(String pathSpec, String path)
474 {
475 if ("".equals(pathSpec))
476 return path;
477
478 char c = pathSpec.charAt(0);
479
480 if (c=='/')
481 {
482 if (pathSpec.length()==1)
483 return null;
484
485 boolean wildcard = isPathWildcardMatch(pathSpec, path);
486
487
488 if (pathSpec.equals(path) && !wildcard)
489 return null;
490
491 if (wildcard)
492 {
493 if (path.length()==pathSpec.length()-2)
494 return null;
495 return path.substring(pathSpec.length()-2);
496 }
497 }
498 return null;
499 }
500
501
502
503
504
505
506
507
508
509 public static String relativePath(String base,
510 String pathSpec,
511 String path )
512 {
513 String info=pathInfo(pathSpec,path);
514 if (info==null)
515 info=path;
516
517 if( info.startsWith( "./"))
518 info = info.substring( 2);
519 if( base.endsWith( URIUtil.SLASH))
520 if( info.startsWith( URIUtil.SLASH))
521 path = base + info.substring(1);
522 else
523 path = base + info;
524 else
525 if( info.startsWith( URIUtil.SLASH))
526 path = base + info;
527 else
528 path = base + URIUtil.SLASH + info;
529 return path;
530 }
531
532
533
534
535 public static class Entry implements Map.Entry
536 {
537 private final Object key;
538 private final Object value;
539 private String mapped;
540 private transient String string;
541
542 Entry(Object key, Object value)
543 {
544 this.key=key;
545 this.value=value;
546 }
547
548 public Object getKey()
549 {
550 return key;
551 }
552
553 public Object getValue()
554 {
555 return value;
556 }
557
558 public Object setValue(Object o)
559 {
560 throw new UnsupportedOperationException();
561 }
562
563 @Override
564 public String toString()
565 {
566 if (string==null)
567 string=key+"="+value;
568 return string;
569 }
570
571 public String getMapped()
572 {
573 return mapped;
574 }
575
576 void setMapped(String mapped)
577 {
578 this.mapped = mapped;
579 }
580 }
581 }