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