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