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.ArrayUtils.*;
016import static org.apache.juneau.internal.StringUtils.*;
017
018import java.lang.reflect.*;
019import java.util.*;
020
021import javax.servlet.http.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.httppart.*;
025import org.apache.juneau.internal.*;
026import org.apache.juneau.json.*;
027import org.apache.juneau.parser.*;
028import org.apache.juneau.rest.annotation.*;
029
030/**
031 * Represents the parsed form-data parameters in an HTTP request.
032 * 
033 * <p>
034 * Similar in functionality to the {@link HttpServletRequest#getParameter(String)} except only looks in the body of the request, not parameters from
035 * the URL query string.
036 * <br>This can be useful in cases where you're using GET parameters on FORM POSTs, and you don't want the body of the request to be read.
037 * 
038 * <p>
039 * Use of this object is incompatible with using any other methods that access the body of the request (since this object will
040 * consume the body).
041 * <br>Some examples:
042 * <ul>
043 *    <li class='jm'>{@link RestRequest#getBody()}
044 *    <li class='jm'>{@link RestRequest#getReader()}
045 *    <li class='jm'>{@link RestRequest#getInputStream()}
046 *    <li class='ja'>{@link Header}
047 * </ul>
048 * 
049 * <h5 class='section'>See Also:</h5>
050 * <ul>
051 *    <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-rest-server.RequestFormData">Overview &gt; juneau-rest-server &gt; RequestFormData</a>
052 * </ul>
053 */
054@SuppressWarnings("unchecked")
055public class RequestFormData extends LinkedHashMap<String,String[]> {
056   private static final long serialVersionUID = 1L;
057
058   private HttpPartParser parser;
059   private BeanSession beanSession;
060
061   RequestFormData setParser(HttpPartParser parser) {
062      this.parser = parser;
063      return this;
064   }
065
066   RequestFormData setBeanSession(BeanSession beanSession) {
067      this.beanSession = beanSession;
068      return this;
069   }
070
071   /**
072    * Adds default entries to these form-data parameters.
073    * 
074    * <p>
075    * This includes the default form-data parameters defined on the resource and method levels.
076    * 
077    * @param defaultEntries 
078    *    The default entries.  
079    *    <br>Can be <jk>null</jk>.
080    * @return This object (for method chaining).
081    */
082   public RequestFormData addDefault(Map<String,Object> defaultEntries) {
083      if (defaultEntries != null) {
084         for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
085            String key = e.getKey();
086            Object value = e.getValue();
087            String[] v = get(key);
088            if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
089               put(key, asStrings(value));
090         }
091      }
092      return this;
093   }
094
095   /**
096    * Adds a default entries to these form-data parameters.
097    * 
098    * <p>
099    * Similar to {@link #put(String, Object)} but doesn't override existing values.
100    * 
101    * @param name 
102    *    The form-data parameter name.  
103    * @param value
104    *    The form-data parameter value.  
105    *    <br>Converted to a String using <code>toString()</code>.
106    *    <br>Ignored if value is <jk>null</jk> or blank.
107    * @return This object (for method chaining).
108    */
109   public RequestFormData addDefault(String name, Object value) {
110      return addDefault(Collections.singletonMap(name, value));
111   }
112
113   /**
114    * Sets a request form-data parameter value.
115    * 
116    * @param name The parameter name.
117    * @param value The parameter value.
118    */
119   public void put(String name, Object value) {
120      super.put(name, asStrings(value));
121   }
122
123   /**
124    * Returns a form-data parameter value.
125    * 
126    * <p>
127    * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
128    * 
129    * <h5 class='section'>Notes:</h5>
130    * <ul class='spaced-list'>
131    *    <li>
132    *       This method returns the raw unparsed value, and differs from calling
133    *       <code>get(name, String.<jk>class</js>)</code> which will convert the value from UON
134    *       notation:
135    *       <ul>
136    *          <li><js>"null"</js> =&gt; <jk>null</jk>
137    *          <li><js>"'null'"</js> =&gt; <js>"null"</js>
138    *          <li><js>"'foo bar'"</js> =&gt; <js>"foo bar"</js>
139    *          <li><js>"foo~~bar"</js> =&gt; <js>"foo~bar"</js>
140    *       </ul>
141    * </ul>
142    * 
143    * @param name The form-data parameter name.
144    * @return The parameter value, or <jk>null</jk> if parameter does not exist.
145    */
146   public String getString(String name) {
147      String[] v = get(name);
148      if (v == null || v.length == 0)
149         return null;
150
151      // Fix for behavior difference between Tomcat and WAS.
152      // getParameter("foo") on "&foo" in Tomcat returns "".
153      // getParameter("foo") on "&foo" in WAS returns null.
154      if (v.length == 1 && v[0] == null)
155         return "";
156
157      return v[0];
158   }
159
160   /**
161    * Same as {@link #getString(String)} except returns a default value if <jk>null</jk> or empty.
162    * 
163    * @param name The form-data parameter name.
164    * @param def The default value.
165    * @return The parameter value, or the default value if parameter does not exist or is <jk>null</jk> or empty.
166    */
167   public String getString(String name, String def) {
168      String s = getString(name);
169      return StringUtils.isEmpty(s) ? def : s;
170   }
171
172   /**
173    * Same as {@link #getString(String)} but converts the value to an integer.
174    * 
175    * @param name The form-data parameter name.
176    * @return The parameter value, or <code>0</code> if parameter does not exist or is <jk>null</jk> or empty.
177    */
178   public int getInt(String name) {
179      return getInt(name, 0);
180   }
181
182   /**
183    * Same as {@link #getString(String,String)} but converts the value to an integer.
184    * 
185    * @param name The form-data parameter name.
186    * @param def The default value.
187    * @return The parameter value, or the default value if parameter does not exist or is <jk>null</jk> or empty.
188    */
189   public int getInt(String name, int def) {
190      String s = getString(name);
191      return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
192   }
193
194   /**
195    * Same as {@link #getString(String)} but converts the value to a boolean.
196    * 
197    * @param name The form-data parameter name.
198    * @return The parameter value, or <jk>false</jk> if parameter does not exist or is <jk>null</jk> or empty.
199    */
200   public boolean getBoolean(String name) {
201      return getBoolean(name, false);
202   }
203
204   /**
205    * Same as {@link #getString(String,String)} but converts the value to a boolean.
206    * 
207    * @param name The form-data parameter name.
208    * @param def The default value.
209    * @return The parameter value, or the default value if parameter does not exist or is <jk>null</jk> or empty.
210    */
211   public boolean getBoolean(String name, boolean def) {
212      String s = getString(name);
213      return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
214   }
215
216   /**
217    * Returns the specified form-data parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
218    * 
219    * <h5 class='section'>Examples:</h5>
220    * <p class='bcode'>
221    *    <jc>// Parse into an integer.</jc>
222    *    <jk>int</jk> myparam = formData.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
223    * 
224    *    <jc>// Parse into an int array.</jc>
225    *    <jk>int</jk>[] myparam = formData.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
226
227    *    <jc>// Parse into a bean.</jc>
228    *    MyBean myparam = formData.get(<js>"myparam"</js>, MyBean.<jk>class</jk>);
229    * 
230    *    <jc>// Parse into a linked-list of objects.</jc>
231    *    List myparam = formData.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
232    * 
233    *    <jc>// Parse into a map of object keys/values.</jc>
234    *    Map myparam = formData.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
235    * </p>
236    * 
237    * <h5 class='section'>See Also:</h5>
238    * <ul>
239    *    <li class='jf'>{@link RestContext#REST_partParser}
240    * </ul>
241    * 
242    * @param name The parameter name.
243    * @param type The class type to convert the parameter value to.
244    * @param <T> The class type to convert the parameter value to.
245    * @return The parameter value converted to the specified class type.
246    * @throws ParseException
247    */
248   public <T> T get(String name, Class<T> type) throws ParseException {
249      return get(null, name, type);
250   }
251
252   /**
253    * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
254    * 
255    * @param parser
256    *    The parser to use for parsing the string value.
257    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
258    * @param name The parameter name.
259    * @param type The class type to convert the parameter value to.
260    * @param <T> The class type to convert the parameter value to.
261    * @return The parameter value converted to the specified class type.
262    * @throws ParseException
263    */
264   public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
265      return parse(parser, name, getClassMeta(type));
266   }
267
268   /**
269    * Same as {@link #get(String, Class)} except returns a default value if not specified.
270    * 
271    * @param name The parameter name.
272    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
273    * @param type The class type to convert the parameter value to.
274    * @param <T> The class type to convert the parameter value to.
275    * @return The parameter value converted to the specified class type.
276    * @throws ParseException
277    */
278   public <T> T get(String name, T def, Class<T> type) throws ParseException {
279      return get(null, name, def, type);
280   }
281
282   /**
283    * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
284    * 
285    * @param parser
286    *    The parser to use for parsing the string value.
287    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
288    * @param name The parameter name.
289    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
290    * @param type The class type to convert the parameter value to.
291    * @param <T> The class type to convert the parameter value to.
292    * @return The parameter value converted to the specified class type.
293    * @throws ParseException
294    */
295   public <T> T get(HttpPartParser parser, String name, T def, Class<T> type) throws ParseException {
296      return parse(parser, name, def, getClassMeta(type));
297   }
298
299   /**
300    * Same as {@link #get(String, Class)} except for use on multi-part parameters
301    * (e.g. <js>"key=1&amp;key=2&amp;key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
302    * 
303    * <p>
304    * This method must only be called when parsing into classes of type Collection or array.
305    * 
306    * @param name The parameter name.
307    * @param type The class type to convert the parameter value to.
308    * @return The parameter value converted to the specified class type.
309    * @throws ParseException
310    */
311   public <T> T getAll(String name, Class<T> type) throws ParseException {
312      return getAll(null, name, type);
313   }
314
315   /**
316    * Same as {@link #getAll(String, Class)} but allows you to override the part parser.
317    * 
318    * <p>
319    * This method must only be called when parsing into classes of type Collection or array.
320    * 
321    * @param parser
322    *    The parser to use for parsing the string value.
323    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
324    * @param name The parameter name.
325    * @param type The class type to convert the parameter value to.
326    * @return The parameter value converted to the specified class type.
327    * @throws ParseException
328    */
329   public <T> T getAll(HttpPartParser parser, String name, Class<T> type) throws ParseException {
330      return parseAll(parser, name, getClassMeta(type));
331   }
332
333   /**
334    * Returns the specified form-data parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
335    * 
336    * <p>
337    * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created.
338    * 
339    * <h5 class='section'>Examples:</h5>
340    * <p class='bcode'>
341    *    <jc>// Parse into a linked-list of strings.</jc>
342    *    List&lt;String&gt; myparam = formData.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
343    * 
344    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
345    *    List&lt;List&lt;String&gt;&gt; myparam = formData.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
346    * 
347    *    <jc>// Parse into a map of string keys/values.</jc>
348    *    Map&lt;String,String&gt; myparam = formData.getr(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
349    * 
350    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
351    *    Map&lt;String,List&lt;MyBean&gt;&gt; myparam = formData.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
352    * </p>
353    * 
354    * <h5 class='section'>Notes:</h5>
355    * <ul class='spaced-list'>
356    *    <li>
357    *       <code>Collections</code> must be followed by zero or one parameter representing the value type.
358    *    <li>
359    *       <code>Maps</code> must be followed by zero or two parameters representing the key and value types.
360    * </ul>
361    * 
362    * <h5 class='section'>See Also:</h5>
363    * <ul>
364    *    <li class='jf'>{@link RestContext#REST_partParser}
365    * </ul>
366    * 
367    * @param name The parameter name.
368    * @param type
369    *    The type of object to create.
370    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
371    * @param args
372    *    The type arguments of the class if it's a collection or map.
373    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
374    *    <br>Ignored if the main type is not a map or collection.
375    * @return The parameter value converted to the specified class type.
376    * @throws ParseException
377    */
378   public <T> T get(String name, Type type, Type...args) throws ParseException {
379      return get(null, name, type, args);
380   }
381
382   /**
383    * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser.
384    * 
385    * @param parser
386    *    The parser to use for parsing the string value.
387    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
388    * @param name The parameter name.
389    * @param type
390    *    The type of object to create.
391    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
392    * @param args
393    *    The type arguments of the class if it's a collection or map.
394    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
395    *    <br>Ignored if the main type is not a map or collection.
396    * @return The parameter value converted to the specified class type.
397    * @throws ParseException
398    */
399   public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
400      return (T)parse(parser, name, getClassMeta(type, args));
401   }
402
403   /**
404    * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
405    * (e.g. <js>"key=1&amp;key=2&amp;key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
406    * 
407    * <p>
408    * This method must only be called when parsing into classes of type Collection or array.
409    * 
410    * @param name The parameter name.
411    * @param type
412    *    The type of object to create.
413    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
414    * @param args
415    *    The type arguments of the class if it's a collection or map.
416    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
417    *    <br>Ignored if the main type is not a map or collection.
418    * @return The parameter value converted to the specified class type.
419    * @throws ParseException
420    */
421   public <T> T getAll(String name, Type type, Type...args) throws ParseException {
422      return getAll(null, name, type, args);
423   }
424   
425   /**
426    * Same as {@link #getAll(String, Type, Type...)} but allows you to override the part parser.
427    * 
428    * @param parser
429    *    The parser to use for parsing the string value.
430    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method. 
431    * @param name The parameter name.
432    * @param type
433    *    The type of object to create.
434    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
435    * @param args
436    *    The type arguments of the class if it's a collection or map.
437    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
438    *    <br>Ignored if the main type is not a map or collection.
439    * @return The parameter value converted to the specified class type.
440    * @throws ParseException
441    */
442   public <T> T getAll(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
443      return (T)parseAll(parser, name, getClassMeta(type, args));
444   }
445   
446
447   /* Workhorse method */
448   <T> T parse(HttpPartParser parser, String name, T def, ClassMeta<T> cm) throws ParseException {
449      String val = getString(name);
450      if (val == null)
451         return def;
452      return parseValue(parser, val, cm);
453   }
454
455   /* Workhorse method */
456   <T> T parse(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
457      String val = getString(name);
458      if (cm.isPrimitive() && (val == null || val.isEmpty()))
459         return cm.getPrimitiveDefault();
460      return parseValue(parser, val, cm);
461   }
462
463   /* Workhorse method */
464   @SuppressWarnings("rawtypes")
465   <T> T parseAll(HttpPartParser parser, String name, ClassMeta<T> cm) throws ParseException {
466      String[] p = get(name);
467      if (p == null)
468         return null;
469      if (parser == null)
470         parser = this.parser;
471      if (cm.isArray()) {
472         List c = new ArrayList();
473         for (int i = 0; i < p.length; i++)
474            c.add(parseValue(parser, p[i], cm.getElementType()));
475         return (T)toArray(c, cm.getElementType().getInnerClass());
476      } else if (cm.isCollection()) {
477         try {
478            Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
479            for (int i = 0; i < p.length; i++)
480               c.add(parseValue(parser, p[i], cm.getElementType()));
481            return (T)c;
482         } catch (ParseException e) {
483            throw e;
484         } catch (Exception e) {
485            // Typically an instantiation exception.
486            throw new ParseException(e);
487         }
488      }
489      throw new ParseException("Invalid call to getParameters(String, ClassMeta).  Class type must be a Collection or array.");
490   }
491
492   private <T> T parseValue(HttpPartParser parser, String val, ClassMeta<T> c) throws ParseException {
493      try {
494         if (parser == null)
495            parser = this.parser;
496         return parser.parse(HttpPartType.FORM_DATA, val, c);
497      } catch (Exception e) {
498         throw new ParseException(e);
499      }
500   }
501
502   /**
503    * Converts the form-data parameters to a readable string.
504    * 
505    * @param sorted Sort the form-data parameters by name.
506    * @return A JSON string containing the contents of the form-data parameters.
507    */
508   public String toString(boolean sorted) {
509      Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : new LinkedHashMap<String,Object>());
510      for (Map.Entry<String,String[]> e : this.entrySet()) {
511         String[] v = e.getValue();
512         m.put(e.getKey(), v.length == 1 ? v[0] : v);
513      }
514      return JsonSerializer.DEFAULT_LAX.toString(m);
515   }
516
517   private ClassMeta<?> getClassMeta(Type type, Type...args) {
518      return beanSession.getClassMeta(type, args);
519   }
520
521   private <T> ClassMeta<T> getClassMeta(Class<T> type) {
522      return beanSession.getClassMeta(type);
523   }
524
525   @Override /* Object */
526   public String toString() {
527      return toString(false);
528   }
529}