KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > dispatch > UrlMap


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.server.dispatch;
31
32 import com.caucho.server.util.CauchoSystem;
33 import com.caucho.util.CharBuffer;
34
35 import javax.servlet.ServletException JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.regex.Matcher JavaDoc;
38 import java.util.regex.Pattern JavaDoc;
39 import java.util.regex.PatternSyntaxException JavaDoc;
40
41 /**
42  * Maps uris to objects, using the syntax in the servlet2.2 deployment
43  * descriptors:
44  *
45  * /foo/bar -- exact match
46  * /foo/bar/* -- matches anything with the /foo/bar prefix
47  * *.jsp -- matches anything with the .jsp suffix
48  */

49 public class UrlMap<E> {
50   // List of matching regular expressions
51
private ArrayList JavaDoc<RegexpEntry<E>> _regexps;
52   // If true, use the shortest match
53
private boolean _bestShort;
54
55   /**
56    * Create a new map
57    */

58   public UrlMap()
59   {
60     _regexps = new ArrayList JavaDoc<RegexpEntry<E>>();
61   }
62   
63   /**
64    * Create a new map preferring a short match.
65    *
66    * @param bestShort if true, use the shortest match
67    */

68   public UrlMap(boolean bestShort)
69   {
70     _regexps = new ArrayList JavaDoc<RegexpEntry<E>>();
71     _bestShort = bestShort;
72   }
73
74   /**
75    * If set to true, this map uses the shortest match instead of the
76    * longest.
77    *
78    * @param bestShort if true, use the shortest match
79    */

80   void setBestShort(boolean bestShort)
81   {
82     _bestShort = bestShort;
83   }
84
85   int size()
86   {
87     return _regexps.size();
88   }
89   
90   public void addMap(String JavaDoc pattern, E value)
91     throws PatternSyntaxException JavaDoc
92   {
93     addMap(pattern, null, value);
94   }
95
96   /**
97    * Adds a new url-pattern and its corresponding value to the map
98    *
99    * @param pattern servlet2.2 url-pattern
100    * @param value object stored as the value
101    */

102   public void addMap(String JavaDoc pattern, String JavaDoc flags, E value)
103     throws PatternSyntaxException JavaDoc
104   {
105     if (pattern.length() == 0 ||
106         pattern.length() == 1 && pattern.charAt(0) == '/') {
107       addRegexp(-1, "", flags, value, true);
108       return;
109     }
110
111     else if (pattern.equals("/*")) {
112       addRegexp(1, "/*", "", flags, value, true);
113       return;
114     }
115
116     int length = pattern.length();
117     boolean isExact = true;
118       
119     if (pattern.charAt(0) != '/' && pattern.charAt(0) != '*') {
120       pattern = "/" + pattern;
121       length++;
122     }
123
124     int prefixLength = -1;
125     boolean isShort = false;
126     CharBuffer cb = new CharBuffer();
127     cb.append("^");
128     for (int i = 0; i < length; i++) {
129       char ch = pattern.charAt(i);
130       
131       if (ch == '*' && i + 1 == length && i > 0) {
132         isExact = false;
133
134         if (pattern.charAt(i - 1) == '/') {
135           cb.setLength(cb.length() - 1);
136
137           if (prefixLength < 0)
138             prefixLength = i - 1;
139           
140         }
141         else if (prefixLength < 0)
142           prefixLength = i;
143         
144         if (prefixLength == 0)
145           prefixLength = 1;
146       }
147       else if (ch == '*') {
148         isExact = false;
149         cb.append(".*");
150         if (prefixLength < 0)
151           prefixLength = i;
152         
153         if (i == 0)
154           isShort = true;
155       }
156       else if (ch == '.' || ch == '[' || ch == '^' || ch == '$' ||
157                ch == '{' || ch == '}' || ch == '|' ||
158                ch == '(' || ch == ')' || ch == '?') {
159         cb.append('\\');
160         cb.append(ch);
161       }
162       else
163         cb.append(ch);
164     }
165
166     if (isExact)
167       cb.append("$");
168     else {
169       cb.append("(?=/)|" + cb.toString() + "\\z");
170     }
171
172     if (prefixLength < 0)
173       prefixLength = pattern.length();
174     else if (prefixLength < pattern.length() &&
175              pattern.charAt(prefixLength) == '/')
176       prefixLength--;
177
178     if (cb.length() > 0 && cb.charAt(0) == '/')
179       cb.insert(0, '^');
180
181     addRegexp(prefixLength, pattern, cb.close(), flags, value, isShort);
182   }
183
184   public static String JavaDoc urlPatternToRegexpPattern(String JavaDoc pattern)
185   {
186     if (pattern.length() == 0 ||
187         pattern.length() == 1 && pattern.charAt(0) == '/') {
188       return "^.*$";
189     }
190
191     else if (pattern.equals("/*"))
192       return "^.*$";
193
194     int length = pattern.length();
195       
196     if (pattern.charAt(0) != '/' && pattern.charAt(0) != '*') {
197       pattern = "/" + pattern;
198       length++;
199     }
200
201     boolean isExact = true;
202     CharBuffer cb = new CharBuffer();
203     cb.append("^");
204     for (int i = 0; i < length; i++) {
205       char ch = pattern.charAt(i);
206       
207       if (ch == '*' && i + 1 == length && i > 0) {
208         isExact = false;
209
210         if (pattern.charAt(i - 1) == '/') {
211           cb.setLength(cb.length() - 1);
212         }
213       }
214       else if (ch == '*') {
215         isExact = false;
216         cb.append(".*");
217       }
218       else if (ch == '.' || ch == '[' || ch == '^' || ch == '$' ||
219                ch == '{' || ch == '}' || ch == '|' ||
220                ch == '(' || ch == ')' || ch == '?') {
221         cb.append('\\');
222         cb.append(ch);
223       }
224       else
225         cb.append(ch);
226     }
227
228     if (isExact)
229       cb.append("\\z");
230     else
231       cb.append("(?=/)|" + cb.toString() + "\\z");
232
233     if (cb.length() > 0 && cb.charAt(0) == '/')
234       cb.insert(0, '^');
235
236     return cb.close();
237   }
238
239   /**
240    * Adds a new url-pattern and its corresponding value to the map
241    *
242    * @param pattern servlet2.2 url-pattern
243    * @param value object stored as the value
244    */

245   public void addStrictMap(String JavaDoc pattern, String JavaDoc flags,
246                            E value)
247     throws PatternSyntaxException JavaDoc, ServletException JavaDoc
248   {
249     if (pattern.length() == 0 ||
250         pattern.length() == 1 && pattern.charAt(0) == '/') {
251       addRegexp(-1, "^.*$", flags, value, true);
252       return;
253     }
254
255     int length = pattern.length();
256     boolean isExact = true;
257       
258     if (pattern.charAt(0) != '/' && pattern.charAt(0) != '*') {
259       pattern = "/" + pattern;
260       length++;
261     }
262
263     if (pattern.indexOf('*') < pattern.lastIndexOf('*'))
264       throw new ServletException JavaDoc("at most one '*' is allowed");
265
266     int prefixLength = -1;
267     boolean isShort = false;
268     CharBuffer cb = new CharBuffer();
269     cb.append('^');
270
271     for (int i = 0; i < length; i++) {
272       char ch = pattern.charAt(i);
273
274       switch (ch) {
275       case '*':
276         if (i > 0 && i + 1 == length && pattern.charAt(i - 1) == '/') {
277           cb.append(".*");
278         }
279         else if (i == 0 && length > 1 && pattern.charAt(1) == '.' &&
280                  pattern.lastIndexOf('/') < 0) {
281           cb.append(".*");
282         }
283         else
284           throw new ServletException JavaDoc("illegal url-pattern `" + pattern + "'");
285         break;
286         
287       case '.': case '[': case '^': case '$':
288       case '{': case '}': case '|': case '(': case '?':
289         cb.append('\\');
290         cb.append(ch);
291         break;
292
293       default:
294         cb.append(ch);
295       }
296     }
297
298     cb.append("$");
299     
300     addRegexp(prefixLength, pattern, cb.close(), flags, value, isShort);
301   }
302   
303   public void addRegexp(String JavaDoc regexp, String JavaDoc flags, E value)
304     throws PatternSyntaxException JavaDoc
305   {
306     addRegexp(0, regexp, flags, value, false);
307   }
308   
309   public void addRegexp(String JavaDoc regexp, E value)
310     throws PatternSyntaxException JavaDoc
311   {
312     addRegexp(0, regexp, null, value, false);
313   }
314
315   /**
316    * Adds a regular expression to the map.
317    *
318    * @param prefixLength the length of the pattern's mandatory prefix
319    * @param regexp the regexp pattern to add
320    * @param flags regexp flags, like "i" for case insensitive
321    * @param value the value for matching the pattern
322    * @param isShort if true, this regexp expects to be shorter than others
323    */

324   public void addRegexp(int prefixLength, String JavaDoc regexp,
325             String JavaDoc flags, E value, boolean isShort)
326     throws PatternSyntaxException JavaDoc
327   {
328     RegexpEntry<E> entry
329       = new RegexpEntry<E>(prefixLength, regexp, flags, value);
330     
331     for (int i = 0; i < _regexps.size(); i++) {
332       RegexpEntry<E> re = _regexps.get(i);
333
334       if (re.equals(entry)) {
335         _regexps.remove(i);
336         break;
337       }
338     }
339
340     if (isShort)
341       entry.setShortMatch();
342
343     _regexps.add(entry);
344   }
345
346   /**
347    * Adds a regular expression to the map.
348    *
349    * @param prefixLength the length of the pattern's mandatory prefix
350    * @param pattern the regexp pattern to add
351    * @param regexp the regexp pattern to add
352    * @param flags regexp flags, like "i" for case insensitive
353    * @param value the value for matching the pattern
354    * @param isShort if true, this regexp expects to be shorter than others
355    */

356   public void addRegexp(int prefixLength, String JavaDoc pattern,
357                         String JavaDoc regexp, String JavaDoc flags,
358                         E value, boolean isShort)
359     throws PatternSyntaxException JavaDoc
360   {
361     RegexpEntry<E> entry
362       = new RegexpEntry<E>(prefixLength, pattern, regexp, flags, value);
363     
364     for (int i = _regexps.size() - 1; i >= 0; i--) {
365       RegexpEntry<E> re = _regexps.get(i);
366
367       if (re.equals(entry)) {
368         _regexps.remove(i);
369       }
370     }
371
372     if (isShort)
373       entry.setShortMatch();
374
375     _regexps.add(entry);
376   }
377   
378   /**
379    * Finds the best match for the uri. In the case of a servlet dispatch,
380    * match is servletPath and replacement is pathInfo.
381    *
382    * @param uri uri to match
383    *
384    * @return matching object
385    */

386   public E map(String JavaDoc uri)
387   {
388     return map(uri, null);
389   }
390   
391   /**
392    * Finds the best match for the uri. In the case of a servlet dispatch,
393    * match is servletPath and replacement is pathInfo.
394    *
395    * @param uri uri to match
396    * @param vars a list of the regexp variables.
397    *
398    * @return matching object
399    */

400   public E map(String JavaDoc uri, ArrayList JavaDoc<String JavaDoc> vars)
401   {
402     E best = null;
403
404     if (vars != null)
405       vars.add(uri);
406     
407     int bestPrefixLength = -2;
408     int bestMinLength = -2;
409     int bestMaxLength = Integer.MAX_VALUE;
410
411     for (int i = 0; i < _regexps.size(); i++) {
412       RegexpEntry<E> entry = _regexps.get(i);
413
414       if ("plugin_match".equals(entry._value) ||
415       "plugin-match".equals(entry._value))
416     continue;
417       if ("plugin_ignore".equals(entry._value) ||
418       "plugin-ignore".equals(entry._value))
419     continue;
420       if (entry._prefixLength < bestPrefixLength)
421         continue;
422
423       Matcher JavaDoc matcher = entry._regexp.matcher(uri);
424
425       if (! matcher.find())
426         continue;
427
428       int begin = matcher.start();
429       int end = matcher.end();
430
431       int length = end - begin;
432
433       boolean bestShort = entry.isShortMatch();
434
435       // Earlier matches override later ones
436
if (bestPrefixLength < entry._prefixLength || bestMinLength < length) {
437         if (vars != null) {
438           vars.clear();
439
440           vars.add(uri.substring(0, end));
441           for (int j = 1; j <= matcher.groupCount(); j++)
442             vars.add(matcher.group(j));
443         }
444
445         best = entry._value;
446         bestPrefixLength = entry._prefixLength;
447         bestMaxLength = length;
448         if (! entry.isShortMatch())
449           bestMinLength = length;
450         if (entry._prefixLength > bestMinLength)
451           bestMinLength = entry._prefixLength;
452       }
453     }
454
455     return best;
456   }
457
458   /**
459    * Return the matching url patterns.
460    */

461   public ArrayList JavaDoc<String JavaDoc> getURLPatterns()
462   {
463     ArrayList JavaDoc<String JavaDoc> patterns = new ArrayList JavaDoc<String JavaDoc>();
464
465     for (int i = 0; i < _regexps.size(); i++) {
466       RegexpEntry<E> entry = _regexps.get(i);
467
468       String JavaDoc urlPattern = entry.getURLPattern();
469
470       if (urlPattern != null)
471     patterns.add(urlPattern);
472     }
473
474     return patterns;
475   }
476
477   static class RegexpEntry<E> {
478     String JavaDoc _urlPattern;
479     String JavaDoc _pattern;
480     int _flags;
481     Pattern JavaDoc _regexp;
482     E _value;
483     int _prefixLength;
484     boolean _shortMatch;
485
486     RegexpEntry(int prefixLength, String JavaDoc pattern, String JavaDoc flags, E value)
487       throws PatternSyntaxException JavaDoc
488     {
489       this(prefixLength, pattern, pattern, flags, value);
490     }
491     
492     RegexpEntry(int prefixLength, String JavaDoc urlPattern,
493                 String JavaDoc pattern, String JavaDoc flags, E value)
494       throws PatternSyntaxException JavaDoc
495     {
496       _urlPattern = urlPattern;
497       _prefixLength = prefixLength;
498       _pattern = pattern;
499
500       if (flags == null && CauchoSystem.isCaseInsensitive())
501         _flags = Pattern.CASE_INSENSITIVE;
502       else if (flags != null && flags.equals("i"))
503         _flags = Pattern.CASE_INSENSITIVE;
504
505       _regexp = Pattern.compile(pattern, _flags);
506       _value = value;
507     }
508
509     void setShortMatch()
510     {
511       _shortMatch = true;
512     }
513
514     boolean isShortMatch()
515     {
516       return _shortMatch;
517     }
518
519     String JavaDoc getURLPattern()
520     {
521       return _urlPattern;
522     }
523
524     String JavaDoc getPattern()
525     {
526       return _pattern;
527     }
528
529     public int hashCode()
530     {
531       if (_urlPattern != null)
532     return _urlPattern.hashCode();
533       else if (_pattern != null)
534     return _pattern.hashCode();
535       else
536     return 17;
537     }
538       
539     public boolean equals(Object JavaDoc o)
540     {
541       if (! (o instanceof RegexpEntry))
542         return false;
543
544       RegexpEntry re = (RegexpEntry) o;
545
546       if (_urlPattern != null)
547         return _urlPattern.equals(re._urlPattern);
548       else if (_pattern != null)
549         return _pattern.equals(re._pattern);
550       else
551         return false;
552     }
553
554     public String JavaDoc toString()
555     {
556       if (_urlPattern != null)
557     return "RegexpEntry[" + _urlPattern + "]";
558       else if (_pattern != null)
559     return "RegexpEntry[" + _pattern + "]";
560       else
561     return super.toString();
562     }
563   }
564 }
565
Popular Tags