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