001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.lang.reflect.*;
018import java.util.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.httppart.*;
022import org.apache.juneau.parser.*;
023
024/**
025 * Contains information about the matched path on the HTTP request.
026 * 
027 * <p>
028 * Provides access to the matched path variables and path match remainder.
029 * 
030 * <h5 class='section'>See Also:</h5>
031 * <ul>
032 *    <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-rest-server.RequestPathMatch">Overview &gt; juneau-rest-server &gt; RequestPathMatch</a>
033 * </ul>
034 */
035@SuppressWarnings("unchecked")
036public class RequestPathMatch extends TreeMap<String,String> {
037   private static final long serialVersionUID = 1L;
038
039   private HttpPartParser parser;
040   private BeanSession beanSession;
041   private String remainder, pattern;
042
043   RequestPathMatch() {
044      super(String.CASE_INSENSITIVE_ORDER);
045   }
046
047   RequestPathMatch parser(HttpPartParser parser) {
048      this.parser = parser;
049      return this;
050   }
051
052   RequestPathMatch beanSession(BeanSession beanSession) {
053      this.beanSession = beanSession;
054      return this;
055   }
056
057   RequestPathMatch remainder(String remainder) {
058      this.remainder = remainder;
059      return this;
060   }
061
062   RequestPathMatch pattern(String pattern) {
063      this.pattern = pattern;
064      return this;
065   }
066
067   /**
068    * Sets a request query parameter value.
069    * 
070    * @param name The parameter name.
071    * @param value The parameter value.
072    */
073   public void put(String name, Object value) {
074      super.put(name, value.toString());
075   }
076
077   /**
078    * Returns the specified path parameter converted to a String.
079    * 
080    * @param name The path variable name.
081    * @return The parameter value.
082    * @throws ParseException
083    */
084   public String getString(String name) throws ParseException {
085      return parse(parser, name, beanSession.string());
086   }
087
088   /**
089    * Returns the specified path parameter converted to an integer.
090    * 
091    * @param name The path variable name.
092    * @return The parameter value.
093    * @throws ParseException
094    */
095   public int getInt(String name) throws ParseException {
096      return parse(parser, name, beanSession.getClassMeta(int.class));
097   }
098
099   /**
100    * Returns the specified path parameter converted to a boolean.
101    * 
102    * @param name The path variable name.
103    * @return The parameter value.
104    * @throws ParseException
105    */
106   public boolean getBoolean(String name) throws ParseException {
107      return parse(parser, name, beanSession.getClassMeta(boolean.class));
108   }
109
110   /**
111    * Returns the specified path parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
112    * 
113    * <h5 class='section'>Examples:</h5>
114    * <p class='bcode'>
115    *    <jc>// Parse into an integer.</jc>
116    *    <jk>int</jk> myparam = path.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
117    * 
118    *    <jc>// Parse into an int array.</jc>
119    *    <jk>int</jk>[] myparam = path.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
120
121    *    <jc>// Parse into a bean.</jc>
122    *    MyBean myparam = path.get(<js>"myparam"</js>, MyBean.<jk>class</jk>);
123    * 
124    *    <jc>// Parse into a linked-list of objects.</jc>
125    *    List myparam = path.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
126    * 
127    *    <jc>// Parse into a map of object keys/values.</jc>
128    *    Map myparam = path.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
129    * </p>
130    * 
131    * <h5 class='section'>See Also:</h5>
132    * <ul>
133    *    <li class='jf'>{@link RestContext#REST_partParser}
134    * </ul>
135    * 
136    * @param name The attribute name.
137    * @param type The class type to convert the attribute value to.
138    * @param <T> The class type to convert the attribute value to.
139    * @return The attribute value converted to the specified class type.
140    * @throws ParseException
141    */
142   public <T> T get(String name, Class<T> type) throws ParseException {
143      return get(parser, name, type);
144   }
145
146   /**
147    * Same as {@link #get(String, Class)} but allows you to override the part parser.
148    * 
149    * @param parser
150    *    The parser to use for parsing the string value.
151    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
152    * @param name The attribute name.
153    * @param type The class type to convert the attribute value to.
154    * @param <T> The class type to convert the attribute value to.
155    * @return The attribute value converted to the specified class type.
156    * @throws ParseException
157    */
158   public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
159      return parse(parser, name, beanSession.getClassMeta(type));
160   }
161
162   /**
163    * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
164    * 
165    * <p>
166    * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created.
167    * 
168    * <p>
169    * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
170    * 
171    * <h5 class='section'>Examples:</h5>
172    * <p class='bcode'>
173    *    <jc>// Parse into a linked-list of strings.</jc>
174    *    List&lt;String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
175    * 
176    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
177    *    List&lt;List&lt;String&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
178    * 
179    *    <jc>// Parse into a map of string keys/values.</jc>
180    *    Map&lt;String,String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
181    * 
182    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
183    *    Map&lt;String,List&lt;MyBean&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
184    * </p>
185    * 
186    * <h5 class='section'>Notes:</h5>
187    * <ul class='spaced-list'>
188    *    <li>
189    *       <code>Collections</code> must be followed by zero or one parameter representing the value type.
190    *    <li>
191    *       <code>Maps</code> must be followed by zero or two parameters representing the key and value types.
192    * </ul>
193    * 
194    * <h5 class='section'>See Also:</h5>
195    * <ul>
196    *    <li class='jf'>{@link RestContext#REST_partParser}
197    * </ul>
198    * 
199    * @param name The attribute name.
200    * @param type
201    *    The type of object to create.
202    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
203    * @param args
204    *    The type arguments of the class if it's a collection or map.
205    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
206    *    <br>Ignored if the main type is not a map or collection.
207    * @param <T> The class type to convert the attribute value to.
208    * @return The attribute value converted to the specified class type.
209    * @throws ParseException
210    */
211   public <T> T get(String name, Type type, Type...args) throws ParseException {
212      return get(parser, name, type, args);
213   }
214
215   /**
216    * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser.
217    * 
218    * @param parser
219    *    The parser to use for parsing the string value.
220    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
221    * @param name The attribute name.
222    * @param type
223    *    The type of object to create.
224    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
225    * @param args
226    *    The type arguments of the class if it's a collection or map.
227    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
228    *    <br>Ignored if the main type is not a map or collection.
229    * @param <T> The class type to convert the attribute value to.
230    * @return The attribute value converted to the specified class type.
231    * @throws ParseException
232    */
233   public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
234      return (T)parse(parser, name, beanSession.getClassMeta(type, args));
235   }
236   
237   
238   /* Workhorse method */
239   <T> T parse(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
240      if (parser == null)
241         parser = this.parser;
242      Object attr = get(name);
243      T t = null;
244      if (attr != null)
245         t = parser.parse(HttpPartType.PATH, attr.toString(), cm);
246      if (t == null && cm.isPrimitive())
247         return cm.getPrimitiveDefault();
248      return t;
249   }
250
251   /**
252    * Returns the decoded remainder of the URL following any path pattern matches.
253    * 
254    * <p>
255    * The behavior of path remainder is shown below given the path pattern "/foo/*":
256    * <table class='styled'>
257    *    <tr>
258    *       <th>URL</th>
259    *       <th>Path Remainder</th>
260    *    </tr>
261    *    <tr>
262    *       <td><code>/foo</code></td>
263    *       <td><jk>null</jk></td>
264    *    </tr>
265    *    <tr>
266    *       <td><code>/foo/</code></td>
267    *       <td><js>""</js></td>
268    *    </tr>
269    *    <tr>
270    *       <td><code>/foo//</code></td>
271    *       <td><js>"/"</js></td>
272    *    </tr>
273    *    <tr>
274    *       <td><code>/foo///</code></td>
275    *       <td><js>"//"</js></td>
276    *    </tr>
277    *    <tr>
278    *       <td><code>/foo/a/b</code></td>
279    *       <td><js>"a/b"</js></td>
280    *    </tr>
281    *    <tr>
282    *       <td><code>/foo//a/b/</code></td>
283    *       <td><js>"/a/b/"</js></td>
284    *    </tr>
285    *    <tr>
286    *       <td><code>/foo/a%2Fb</code></td>
287    *       <td><js>"a/b"</js></td>
288    *    </tr>
289    * </table>
290    * 
291    * <h5 class='section'>Example:</h5>
292    * <p class='bcode'>
293    *    <jc>// REST method</jc>
294    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>,path=<js>"/foo/{bar}/*"</js>)
295    *    <jk>public</jk> String doGetById(RequestPathMatch path, <jk>int</jk> bar) {
296    *       <jk>return</jk> path.getRemainder();
297    *    }
298    * </p>
299    * 
300    * @return The path remainder string.
301    */
302   public String getRemainder() {
303      return urlDecode(remainder);
304   }
305
306   /**
307    * Same as {@link #getRemainder()} but doesn't decode characters.
308    * 
309    * @return The un-decoded path remainder.
310    */
311   public String getRemainderUndecoded() {
312      return remainder;
313   }
314   
315   /**
316    * Returns the path pattern that matched this request.
317    * 
318    * @return The path pattern that matched this request.
319    */
320   public String getPattern() {
321      return pattern;
322   }
323}