KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > PathMap


1 // ========================================================================
2
// $Id: PathMap.java,v 1.25 2005/08/13 00:01:24 gregwilkins Exp $
3
// Copyright 1999-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.http;
17
18 import java.io.Externalizable JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24
25 import org.apache.commons.logging.Log;
26 import org.mortbay.log.LogFactory;
27 import org.mortbay.util.LazyList;
28 import org.mortbay.util.SingletonList;
29 import org.mortbay.util.StringMap;
30
31 /* ------------------------------------------------------------ */
32 /** URI path map to Object.
33  * This mapping implements the path specification recommended
34  * in the 2.2 Servlet API.
35  *
36  * Path specifications can be of the following forms:<PRE>
37  * /foo/bar - an exact path specification.
38  * /foo/* - a prefix path specification (must end '/*').
39  * *.ext - a suffix path specification.
40  * / - the default path specification.
41  * </PRE>
42  * Matching is performed in the following order <NL>
43  * <LI>Exact match.
44  * <LI>Longest prefix match.
45  * <LI>Longest suffix match.
46  * <LI>default.
47  * </NL>
48  * Multiple path specifications can be mapped by providing a list of
49  * specifications. The list is separated by the characters specified
50  * in the "org.mortbay.http.PathMap.separators" System property, which
51  * defaults to :
52  * <P>
53  * Note that this is a very different mapping to that provided by PathMap
54  * in Jetty2.
55  * <P>
56  * This class is not synchronized for get's. If concurrent modifications are
57  * possible then it should be synchronized at a higher level.
58  *
59  * @version $Id: PathMap.java,v 1.25 2005/08/13 00:01:24 gregwilkins Exp $
60  * @author Greg Wilkins (gregw)
61  */

62 public class PathMap extends HashMap JavaDoc implements Externalizable JavaDoc
63 {
64     private static Log log = LogFactory.getLog(PathMap.class);
65     
66     /* ------------------------------------------------------------ */
67     private static String JavaDoc __pathSpecSeparators =
68         System.getProperty("org.mortbay.http.PathMap.separators",":,");
69     
70     /* ------------------------------------------------------------ */
71     /** Set the path spec separator.
72      * Multiple path specification may be included in a single string
73      * if they are separated by the characters set in this string.
74      * The default value is ":," or whatever has been set by the
75      * system property org.mortbay.http.PathMap.separators
76      * @param s separators
77      */

78     public static void setPathSpecSeparators(String JavaDoc s)
79     {
80         __pathSpecSeparators=s;
81     }
82     
83     /* --------------------------------------------------------------- */
84     StringMap _prefixMap=new StringMap();
85     StringMap _suffixMap=new StringMap();
86     StringMap _exactMap=new StringMap();
87
88     List JavaDoc _defaultSingletonList=null;
89     Map.Entry JavaDoc _prefixDefault=null;
90     Map.Entry JavaDoc _default=null;
91     Set JavaDoc _entrySet;
92     boolean _nodefault=false;
93     
94     /* --------------------------------------------------------------- */
95     /** Construct empty PathMap.
96      */

97     public PathMap()
98     {
99         super(11);
100         _entrySet=entrySet();
101     }
102
103     /* --------------------------------------------------------------- */
104     /** Construct empty PathMap.
105      */

106     public PathMap(boolean nodefault)
107     {
108         super(11);
109         _entrySet=entrySet();
110         _nodefault=nodefault;
111     }
112     
113     /* --------------------------------------------------------------- */
114     /** Construct empty PathMap.
115      */

116     public PathMap(int capacity)
117     {
118         super (capacity);
119         _entrySet=entrySet();
120     }
121     
122     /* --------------------------------------------------------------- */
123     /** Construct from dictionary PathMap.
124      */

125     public PathMap(Map JavaDoc m)
126     {
127         putAll(m);
128         _entrySet=entrySet();
129     }
130     
131     /* ------------------------------------------------------------ */
132     public void writeExternal(java.io.ObjectOutput JavaDoc out)
133         throws java.io.IOException JavaDoc
134     {
135         HashMap JavaDoc map = new HashMap JavaDoc(this);
136         out.writeObject(map);
137     }
138     
139     /* ------------------------------------------------------------ */
140     public void readExternal(java.io.ObjectInput JavaDoc in)
141         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc
142     {
143         HashMap JavaDoc map = (HashMap JavaDoc)in.readObject();
144         this.putAll(map);
145     }
146     
147     /* --------------------------------------------------------------- */
148     /** Add a single path match to the PathMap.
149      * @param pathSpec The path specification, or comma separated list of
150      * path specifications.
151      * @param object The object the path maps to
152      */

153     public synchronized Object JavaDoc put(Object JavaDoc pathSpec, Object JavaDoc object)
154     {
155         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(pathSpec.toString(),__pathSpecSeparators);
156         Object JavaDoc old =null;
157         
158         while (tok.hasMoreTokens())
159         {
160             String JavaDoc spec=tok.nextToken();
161             
162             if (!spec.startsWith("/") && !spec.startsWith("*."))
163             {
164                 log.warn("PathSpec "+spec+". must start with '/' or '*.'");
165                 spec="/"+spec;
166             }
167             
168             old = super.put(spec,object);
169             
170             // Make entry that was just created.
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                     _prefixMap.put(spec.substring(0,spec.length()-2),entry);
180                     _exactMap.put(spec.substring(0,spec.length()-1),entry);
181                     _exactMap.put(spec.substring(0,spec.length()-2),entry);
182                 }
183                 else if (spec.startsWith("*."))
184                     _suffixMap.put(spec.substring(2),entry);
185                 else if (spec.equals("/"))
186                 {
187                     if (_nodefault)
188                         _exactMap.put(spec,entry);
189                     else
190                     {
191                         _default=entry;
192                         _defaultSingletonList=
193                             SingletonList.newSingletonList(_default);
194                     }
195                 }
196                 else
197                     _exactMap.put(spec,entry);
198             }
199         }
200             
201         return old;
202     }
203
204     /* ------------------------------------------------------------ */
205     /** Get object matched by the path.
206      * @param path the path.
207      * @return Best matched object or null.
208      */

209     public Object JavaDoc match(String JavaDoc path)
210     {
211         Map.Entry JavaDoc entry = getMatch(path);
212         if (entry!=null)
213             return entry.getValue();
214         return null;
215     }
216     
217     
218     /* --------------------------------------------------------------- */
219     /** Get the entry mapped by the best specification.
220      * @param path the path.
221      * @return Map.Entry of the best matched or null.
222      */

223     public Map.Entry JavaDoc getMatch(String JavaDoc path)
224     {
225         Map.Entry JavaDoc entry;
226
227         if (path==null)
228             return null;
229         
230         int l=path.indexOf(';');
231         if (l<0)
232         {
233             l=path.indexOf('?');
234             if (l<0)
235                 l=path.length();
236         }
237
238         // try exact match
239
entry=_exactMap.getEntry(path,0,l);
240         if (entry!=null)
241             return (Map.Entry JavaDoc) entry.getValue();
242         
243         // prefix search
244
int i=l;
245         while((i=path.lastIndexOf('/',i-1))>=0)
246         {
247             entry=_prefixMap.getEntry(path,0,i);
248             if (entry!=null)
249                 return (Map.Entry JavaDoc) entry.getValue();
250         }
251         
252         // Prefix Default
253
if (_prefixDefault!=null)
254             return _prefixDefault;
255         
256         // Extension search
257
i=0;
258         while ((i=path.indexOf('.',i+1))>0)
259         {
260             entry=_suffixMap.getEntry(path,i+1,l-i-1);
261             if (entry!=null)
262                 return (Map.Entry JavaDoc) entry.getValue();
263         }
264         
265         // Default
266
return _default;
267     }
268     
269     /* --------------------------------------------------------------- */
270     /** Get all entries matched by the path.
271      * Best match first.
272      * @param path Path to match
273      * @return List of Map.Entry instances key=pathSpec
274      */

275     public List JavaDoc getMatches(String JavaDoc path)
276     {
277         Map.Entry JavaDoc entry;
278         Object JavaDoc entries=null;
279
280         if (path==null)
281             return LazyList.getList(entries);
282         
283         int l=path.indexOf(';');
284         if (l<0)
285         {
286             l=path.indexOf('?');
287             if (l<0)
288                 l=path.length();
289         }
290
291         // try exact match
292
entry=_exactMap.getEntry(path,0,l);
293         if (entry!=null)
294             entries=LazyList.add(entries,entry.getValue());
295         
296         // prefix search
297
int i=l-1;
298         while((i=path.lastIndexOf('/',i-1))>=0)
299         {
300             entry=_prefixMap.getEntry(path,0,i);
301             if (entry!=null)
302                 entries=LazyList.add(entries,entry.getValue());
303         }
304         
305         // Prefix Default
306
if (_prefixDefault!=null)
307             entries=LazyList.add(entries,_prefixDefault);
308         
309         // Extension search
310
i=0;
311         while ((i=path.indexOf('.',i+1))>0)
312         {
313             entry=_suffixMap.getEntry(path,i+1,l-i-1);
314             if (entry!=null)
315                 entries=LazyList.add(entries,entry.getValue());
316         }
317
318         // Default
319
if (_default!=null)
320         {
321             // Optimization for just the default
322
if (entries==null)
323                 return _defaultSingletonList;
324             
325             entries=LazyList.add(entries,_default);
326         }
327         
328         return LazyList.getList(entries);
329     }
330
331
332     /* --------------------------------------------------------------- */
333     public synchronized Object JavaDoc remove(Object JavaDoc pathSpec)
334     {
335         if (pathSpec!=null)
336         {
337             String JavaDoc spec=(String JavaDoc) pathSpec;
338             if (spec.equals("/*"))
339                 _prefixDefault=null;
340             else if (spec.endsWith("/*"))
341             {
342                 _prefixMap.remove(spec.substring(0,spec.length()-2));
343                 _exactMap.remove(spec.substring(0,spec.length()-1));
344                 _exactMap.remove(spec.substring(0,spec.length()-2));
345             }
346             else if (spec.startsWith("*."))
347                 _suffixMap.remove(spec.substring(2));
348             else if (spec.equals("/"))
349             {
350                 _default=null;
351                 _defaultSingletonList=null;
352             }
353             else
354                 _exactMap.remove(spec);
355         }
356         return super.remove(pathSpec);
357     }
358     
359     /* --------------------------------------------------------------- */
360     public void clear()
361     {
362         _exactMap=new StringMap();
363         _prefixMap=new StringMap();
364         _suffixMap=new StringMap();
365         _default=null;
366         _defaultSingletonList=null;
367         super.clear();
368     }
369     
370     /* --------------------------------------------------------------- */
371     /**
372      * @return true if match.
373      */

374     public static boolean match(String JavaDoc pathSpec, String JavaDoc path)
375         throws IllegalArgumentException JavaDoc
376     {
377         char c = pathSpec.charAt(0);
378         if (c=='/')
379         {
380             if (pathSpec.length()==1 || pathSpec.equals(path))
381                 return true;
382             
383             if (pathSpec.endsWith("/*") &&
384                 pathSpec.regionMatches(0,path,0,pathSpec.length()-2))
385                 return true;
386             
387             if (path.startsWith(pathSpec) && path.charAt(pathSpec.length())==';')
388                 return true;
389         }
390         else if (c=='*')
391             return path.regionMatches(path.length()-pathSpec.length()+1,
392                                       pathSpec,1,pathSpec.length()-1);
393         return false;
394     }
395
396     /* --------------------------------------------------------------- */
397     /**
398      * @return true if match.
399      */

400     public static boolean match(String JavaDoc pathSpec, String JavaDoc path, boolean noDefault)
401         throws IllegalArgumentException JavaDoc
402     {
403         char c = pathSpec.charAt(0);
404         if (c=='/')
405         {
406             if (!noDefault && pathSpec.length()==1 || pathSpec.equals(path))
407                 return true;
408             
409             if (pathSpec.endsWith("/*") &&
410                 pathSpec.regionMatches(0,path,0,pathSpec.length()-2))
411                 return true;
412             
413             if (path.startsWith(pathSpec) && path.charAt(pathSpec.length())==';')
414                 return true;
415         }
416         else if (c=='*')
417             return path.regionMatches(path.length()-pathSpec.length()+1,
418                                       pathSpec,1,pathSpec.length()-1);
419         return false;
420     }
421     
422     /* --------------------------------------------------------------- */
423     /** Return the portion of a path that matches a path spec.
424      * @return null if no match at all.
425      */

426     public static String JavaDoc pathMatch(String JavaDoc pathSpec, String JavaDoc path)
427     {
428         char c = pathSpec.charAt(0);
429         
430         if (c=='/')
431         {
432             if (pathSpec.length()==1)
433                 return path;
434         
435             if (pathSpec.equals(path))
436                 return path;
437             
438             if (pathSpec.endsWith("/*") &&
439                 pathSpec.regionMatches(0,path,0,pathSpec.length()-2))
440                 return path.substring(0,pathSpec.length()-2);
441             
442             if (path.startsWith(pathSpec) && path.charAt(pathSpec.length())==';')
443                 return path;
444         }
445         else if (c=='*')
446         {
447             if (path.regionMatches(path.length()-(pathSpec.length()-1),
448                                    pathSpec,1,pathSpec.length()-1))
449                 return path;
450         }
451         return null;
452     }
453     
454     /* --------------------------------------------------------------- */
455     /** Return the portion of a path that is after a path spec.
456      * @return The path info string
457      */

458     public static String JavaDoc pathInfo(String JavaDoc pathSpec, String JavaDoc path)
459     {
460         char c = pathSpec.charAt(0);
461         
462         if (c=='/')
463         {
464             if (pathSpec.length()==1)
465                 return null;
466             
467             if (pathSpec.equals(path))
468                 return null;
469             
470             if (pathSpec.endsWith("/*") &&
471                 pathSpec.regionMatches(0,path,0,pathSpec.length()-2))
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     /** Relative path.
484      * @param base The base the path is relative to.
485      * @param pathSpec The spec of the path segment to ignore.
486      * @param path the additional path
487      * @return base plus path with pathspec removed
488      */

489     public static String JavaDoc relativePath(String JavaDoc base,
490                                       String JavaDoc pathSpec,
491                                       String JavaDoc path )
492     {
493         String JavaDoc 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( "/"))
500             if( info.startsWith( "/"))
501                 path = base + info.substring(1);
502             else
503                 path = base + info;
504         else
505             if( info.startsWith( "/"))
506                 path = base + info;
507             else
508                 path = base + "/" + info;
509         return path;
510     }
511  
512     /* ------------------------------------------------------------ */
513     /* ------------------------------------------------------------ */
514     /* ------------------------------------------------------------ */
515     private static class Entry implements Map.Entry JavaDoc
516     {
517         private Object JavaDoc key;
518         private Object JavaDoc value;
519         private transient String JavaDoc string;
520
521         Entry(Object JavaDoc key, Object JavaDoc value)
522         {
523             this.key=key;
524             this.value=value;
525         }
526
527         public Object JavaDoc getKey()
528         {
529             return key;
530         }
531         
532         public Object JavaDoc getValue()
533         {
534             return value;
535         }
536
537         public Object JavaDoc setValue(Object JavaDoc o)
538         {
539             throw new UnsupportedOperationException JavaDoc();
540         }
541
542         public String JavaDoc toString()
543         {
544             if (string==null)
545                 string=key+"="+value;
546             return string;
547         }
548     }
549 }
550
Popular Tags