View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.regex.Matcher;
26  import java.util.regex.Pattern;
27  
28  /**
29   * Class representing a Uri Template - with basic {token} format support.
30   * See JAX-RS JSR-311.
31   * 
32   * @author davidc
33   */
34  public class UriTemplate
35  {
36      private static final Pattern VALID_URI = Pattern.compile("^/(([\\w\\-]+|\\{([a-zA-Z][\\w]*)\\})(;*)/?)+(\\.\\w+$)?|^/$");
37      private static final Pattern VARIABLE = Pattern.compile("\\{([a-zA-Z]\\w*)\\}");
38      private static final String VARIABLE_REGEX = "(.*?)";
39  
40      private String template;
41      private Pattern regex;
42      private String[] vars;
43      private int charCnt;
44  
45      /**
46       * Construct
47       * 
48       * @param template
49       */
50      public UriTemplate(String template)
51      {
52          // ensure template is provided
53          if (template == null || template.length() == 0)
54          {
55              throw new WebScriptException("URI Template not provided");
56          }
57  
58          // ensure template is syntactically correct
59          Matcher validMatcher = VALID_URI.matcher(template);
60          if (!validMatcher.matches())
61          {
62              throw new WebScriptException("URI Template malformed: " + template);
63          }
64  
65          // convert uri template into equivalent regular expression
66          // and extract variable names
67          StringBuilder templateRegex = new StringBuilder();
68          List<String> names = new ArrayList<String>();
69          int charCnt = 0;
70          int start = 0;
71          int end = 0;
72          Matcher matcher = VARIABLE.matcher(template);
73          while(matcher.find())
74          {
75              end = matcher.start();
76              charCnt += appendTemplate(template, start, end, templateRegex);
77              templateRegex.append(VARIABLE_REGEX);
78              String name = matcher.group(1);
79              names.add(name);
80              start = matcher.end();
81          }
82          charCnt += appendTemplate(template, start, template.length(), templateRegex);
83  
84          // initialise
85          this.template = template;
86          this.charCnt = charCnt;
87          this.regex = Pattern.compile(templateRegex.toString());
88          this.vars = new String[names.size()];
89          names.toArray(this.vars);
90      }
91  
92      /**
93       * Helper for constructing regular expression (escaping regex chars where necessary)
94       * 
95       * @param template
96       * @param start
97       * @param end
98       * @param regex
99       * @return
100      */
101     private int appendTemplate(String template, int start, int end, StringBuilder regex)
102     {
103         for (int i = start; i < end; i++)
104         {
105             char c = template.charAt(i);
106             if ("(.?)".indexOf(c) != -1)
107             {
108                 regex.append("\\");
109             }
110             regex.append(c);
111         }
112         return end - start;
113     }
114 
115     /**
116      * Determine if uri is matched by this uri template and return a map of variable
117      * values if it does.
118      * 
119      * @param uri  uri to match
120      * @return  map of variable values (or null, if no match, or empty if no vars)
121      */
122     public Map<String, String> match(String uri)
123     {
124         Map<String, String> values = null;
125 
126         if (uri != null && uri.length() != 0)
127         {
128             Matcher m = regex.matcher(uri);
129             if (m.matches())
130             {
131                 values = new HashMap<String, String>(m.groupCount(), 1.0f);
132                 for (int i = 0; i < m.groupCount(); i++)
133                 {
134                     String name = vars[i];
135                     String value = m.group(i + 1);
136                     
137                     /**
138                      * To support the case where multiple tokens of the same name may appear in the url
139                      * there's only a match if the value provided for each instance of the token is the same
140                      * e.g.  /{a}/xyx/{a}  only matches  /fred/xyx/fred  not  /fred/xyx/bob
141                      */
142                     String existingValue = values.get(name);
143                     if (existingValue != null && !existingValue.equals(value))
144                     {
145                         return null;
146                     }
147                     
148                     values.put(vars[i], value);
149                 }
150             }
151         }
152 
153         return values;
154     }
155 
156     /**
157      * @return  get template
158      */
159     public String getTemplate()
160     {
161         return template;
162     }
163 
164     /**
165      * @return  get regular expression equivalent
166      */
167     public Pattern getRegex()
168     {
169         return regex;
170     }
171 
172     /**
173      * @return  get variable names contained in uri template
174      */
175     public String[] getVariableNames()
176     {
177         return vars;
178     }
179 
180     /**
181      * @return  get number of static characters in uri template
182      */
183     public int getStaticCharCount()
184     {
185         return charCnt;
186     }
187 
188     @Override
189     public final String toString()
190     {
191         String strVars = "";
192         for (int i = 0; i < vars.length; i++)
193         {
194             strVars += vars[i];
195             if (i < vars.length -1)
196             {
197                 strVars += ",";
198             }
199         }
200         return regex.toString() + " (vars=[" + strVars + "])"; 
201     }
202 
203     @Override
204     public final int hashCode()
205     {
206         return regex.hashCode();
207     }
208 
209     @Override
210     public final boolean equals(Object obj)
211     {
212         if (!(obj instanceof UriTemplate))
213         {
214             return false;
215         }
216         return regex.equals(((UriTemplate)obj).regex);
217     }
218 }