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.collections.*;
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.*;
031import org.apache.juneau.utils.*;
032
033/**
034 * Represents the query parameters in an HTTP request.
035 *
036 * <p>
037 * Similar in functionality to the {@link HttpServletRequest#getParameter(String)} except only looks in the URL string, not parameters from
038 * URL-Encoded FORM posts.
039 * <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.
040 *
041 * <ul class='seealso'>
042 *    <li class='link'>{@doc RestmRequestQuery}
043 * </ul>
044 */
045@SuppressWarnings("unchecked")
046public final class RequestQuery extends LinkedHashMap<String,String[]> {
047   private static final long serialVersionUID = 1L;
048
049   private final RestRequest req;
050   private HttpPartParserSession parser;
051
052   RequestQuery(RestRequest req) {
053      this.req = req;
054   }
055
056   RequestQuery parser(HttpPartParserSession parser) {
057      this.parser = parser;
058      return this;
059   }
060
061   /*
062    * Create a copy of the request query parameters.
063    */
064   RequestQuery copy() {
065      RequestQuery rq = new RequestQuery(req);
066      rq.putAll(this);
067      return rq;
068   }
069
070   /**
071    * Adds default entries to these query parameters.
072    *
073    * <p>
074    * This includes the default queries defined at the resource and method levels.
075    *
076    * @param defaultEntries
077    *    The default entries.
078    *    <br>Can be <jk>null</jk>.
079    * @return This object (for method chaining).
080    */
081   public RequestQuery addDefault(Map<String,Object> defaultEntries) {
082      if (defaultEntries != null) {
083         for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
084            String key = e.getKey();
085            Object value = e.getValue();
086            String[] v = get(key);
087            if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
088               put(key, stringifyAll(value));
089         }
090      }
091      return this;
092   }
093
094   /**
095    * Adds a default entries to these query parameters.
096    *
097    * <p>
098    * Similar to {@link #put(String, Object)} but doesn't override existing values.
099    *
100    * @param name
101    *    The query parameter name.
102    * @param value
103    *    The query parameter value.
104    *    <br>Converted to a String using <c>toString()</c>.
105    *    <br>Ignored if value is <jk>null</jk> or blank.
106    * @return This object (for method chaining).
107    */
108   public RequestQuery addDefault(String name, Object value) {
109      return addDefault(Collections.singletonMap(name, value));
110   }
111
112   /**
113    * Same as {@link #get(Object)} but allows you to find the query parameter using a case-insensitive match.
114    *
115    * @param name The query parameter name.
116    * @param caseInsensitive If <jk>true</jk> use case-insensitive matching on the query parameter name.
117    * @return The resolved entry, or <jk>null</jk> if not found.
118    */
119   public String[] get(String name, boolean caseInsensitive) {
120      if (! caseInsensitive)
121         return get(name);
122      for (Map.Entry<String,String[]> e : entrySet())
123         if (e.getKey().equalsIgnoreCase(name))
124            return e.getValue();
125      return null;
126   }
127
128   /**
129    * Sets a request query parameter value.
130    *
131    * <p>
132    * This overwrites any existing value.
133    *
134    * @param name The parameter name.
135    * @param value
136    *    The parameter value.
137    *    <br>Can be <jk>null</jk>.
138    */
139   public void put(String name, Object value) {
140      if (value == null)
141         put(name, null);
142      else
143         put(name, stringifyAll(value));
144   }
145
146   /**
147    * Returns a query parameter value as a string.
148    *
149    * <p>
150    * If multiple query parameters have the same name, this returns only the first instance.
151    *
152    * @param name The URL parameter name.
153    * @return
154    *    The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
155    */
156   public String getString(String name) {
157      return getString(name, false);
158   }
159
160   /**
161    * Same as {@link #getString(String)} but allows you to search for the query parameter using case-insensitive matching.
162    *
163    * <p>
164    * If multiple query parameters have the same name, this returns only the first instance.
165    *
166    * @param name The URL parameter name.
167    * @param caseInsensitive If <jk>true</jk> use case insensitive matching on the query parameter name.
168    * @return
169    *    The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
170    */
171   public String getString(String name, boolean caseInsensitive) {
172      String[] v = get(name, caseInsensitive);
173      if (v == null || v.length == 0)
174         return null;
175
176      // Fix for behavior difference between Tomcat and WAS.
177      // getParameter("foo") on "&foo" in Tomcat returns "".
178      // getParameter("foo") on "&foo" in WAS returns null.
179      if (v.length == 1 && v[0] == null)
180         return "";
181
182      return v[0];
183   }
184
185   /**
186    * Same as {@link #getString(String)} but returns the specified default value if the query parameter was not
187    * specified.
188    *
189    * @param name The URL parameter name.
190    * @param def The default value.
191    * @return
192    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
193    */
194   public String getString(String name, String def) {
195      String s = getString(name);
196      return StringUtils.isEmpty(s) ? def : s;
197   }
198
199   /**
200    * Same as {@link #getString(String)} but converts the value to an integer.
201    *
202    * @param name The URL parameter name.
203    * @return
204    *    The parameter value, or <c>0</c> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
205    */
206   public int getInt(String name) {
207      return getInt(name, 0);
208   }
209
210   /**
211    * Same as {@link #getString(String,String)} but converts the value to an integer.
212    *
213    * @param name The URL parameter name.
214    * @param def The default value.
215    * @return
216    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
217    */
218   public int getInt(String name, int def) {
219      String s = getString(name);
220      return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
221   }
222
223   /**
224    * Same as {@link #getString(String)} but converts the value to a boolean.
225    *
226    * @param name The URL parameter name.
227    * @return
228    *    The parameter value, or <jk>false</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
229    */
230   public boolean getBoolean(String name) {
231      return getBoolean(name, false);
232   }
233
234   /**
235    * Same as {@link #getString(String,String)} but converts the value to a boolean.
236    *
237    * @param name The URL parameter name.
238    * @param def The default value.
239    * @return
240    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
241    */
242   public boolean getBoolean(String name, boolean def) {
243      String s = getString(name);
244      return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
245   }
246
247   /**
248    * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
249    *
250    * <h5 class='section'>Examples:</h5>
251    * <p class='bcode w800'>
252    *    <jc>// Parse into an integer.</jc>
253    *    <jk>int</jk> myparam = query.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
254    *
255    *    <jc>// Parse into an int array.</jc>
256    *    <jk>int</jk>[] myparam = query.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
257
258    *    <jc>// Parse into a bean.</jc>
259    *    MyBean myparam = query.get(<js>"myparam"</js>, MyBean.<jk>class</jk>);
260    *
261    *    <jc>// Parse into a linked-list of objects.</jc>
262    *    List myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
263    *
264    *    <jc>// Parse into a map of object keys/values.</jc>
265    *    Map myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
266    * </p>
267    *
268    * <ul class='seealso'>
269    *    <li class='jf'>{@link RestContext#REST_partParser}
270    * </ul>
271    *
272    * @param name The parameter name.
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 BadRequest Thrown if input could not be parsed.
277    * @throws InternalServerError Thrown if any other exception occurs.
278    */
279   public <T> T get(String name, Class<T> type) throws BadRequest, InternalServerError {
280      return getInner(null, null, name, null, getClassMeta(type));
281   }
282
283   /**
284    * Same as {@link #get(String, Class)} but allows you to override the part parser.
285    *
286    * @param parser
287    *    The parser to use for parsing the string value.
288    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
289    * @param schema
290    *    The schema object that defines the format of the input.
291    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
292    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
293    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
294    * @param name The parameter name.
295    * @param type The class type to convert the parameter value to.
296    * @param <T> The class type to convert the parameter value to.
297    * @return The parameter value converted to the specified class type.
298    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
299    * @throws InternalServerError Thrown if any other exception occurs.
300    */
301   public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
302      return getInner(parser, schema, name, null, getClassMeta(type));
303   }
304
305   /**
306    * Same as {@link #get(String, Class)} except returns a default value if not found.
307    *
308    * @param name The parameter name.
309    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
310    * @param type The class type to convert the parameter value to.
311    * @param <T> The class type to convert the parameter value to.
312    * @return The parameter value converted to the specified class type.
313    * @throws BadRequest Thrown if input could not be parsed.
314    * @throws InternalServerError Thrown if any other exception occurs.
315    */
316   public <T> T get(String name, T def, Class<T> type) throws BadRequest, InternalServerError {
317      return getInner(null, null, name, def, getClassMeta(type));
318   }
319
320   /**
321    * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
322    *
323    * @param parser
324    *    The parser to use for parsing the string value.
325    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
326    * @param schema
327    *    The schema object that defines the format of the input.
328    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
329    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
330    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
331    * @param name The parameter name.
332    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
333    * @param type The class type to convert the parameter value to.
334    * @param <T> The class type to convert the parameter value to.
335    * @return The parameter value converted to the specified class type.
336    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
337    * @throws InternalServerError Thrown if any other exception occurs.
338    */
339   public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
340      return getInner(parser, schema, name, def, getClassMeta(type));
341   }
342
343   /**
344    * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
345    *
346    * <p>
347    * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created.
348    *
349    * <h5 class='section'>Examples:</h5>
350    * <p class='bcode w800'>
351    *    <jc>// Parse into a linked-list of strings.</jc>
352    *    List&lt;String&gt; myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
353    *
354    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
355    *    List&lt;List&lt;String&gt;&gt; myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
356    *
357    *    <jc>// Parse into a map of string keys/values.</jc>
358    *    Map&lt;String,String&gt; myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
359    *
360    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
361    *    Map&lt;String,List&lt;MyBean&gt;&gt; myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
362    * </p>
363    *
364    * <ul class='notes'>
365    *    <li>
366    *       <c>Collections</c> must be followed by zero or one parameter representing the value type.
367    *    <li>
368    *       <c>Maps</c> must be followed by zero or two parameters representing the key and value types.
369    * </ul>
370    *
371    * <ul class='seealso'>
372    *    <li class='jf'>{@link RestContext#REST_partParser}
373    * </ul>
374    *
375    * @param name The parameter name.
376    * @param type
377    *    The type of object to create.
378    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
379    * @param args
380    *    The type arguments of the class if it's a collection or map.
381    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
382    *    <br>Ignored if the main type is not a map or collection.
383    * @param <T> The class type to convert the parameter value to.
384    * @return The parameter value converted to the specified class type.
385    * @throws BadRequest Thrown if input could not be parsed.
386    * @throws InternalServerError Thrown if any other exception occurs.
387    */
388   public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError {
389      return getInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
390   }
391
392   /**
393    * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser.
394    *
395    * @param parser
396    *    The parser to use for parsing the string value.
397    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
398    * @param schema
399    *    The schema object that defines the format of the input.
400    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
401    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
402    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
403    * @param name The parameter name.
404    * @param type
405    *    The type of object to create.
406    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
407    * @param args
408    *    The type arguments of the class if it's a collection or map.
409    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
410    *    <br>Ignored if the main type is not a map or collection.
411    * @param <T> The class type to convert the parameter value to.
412    * @return The parameter value converted to the specified class type.
413    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
414    * @throws InternalServerError Thrown if any other exception occurs.
415    */
416   public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
417      return getInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
418   }
419
420   /**
421    * Same as {@link #get(String, Class)} except returns a default value if not found.
422    *
423    * @param name The parameter name.
424    * @param type
425    *    The type of object to create.
426    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
427    * @param args
428    *    The type arguments of the class if it's a collection or map.
429    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
430    *    <br>Ignored if the main type is not a map or collection.
431    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
432    * @param <T> The class type to convert the parameter value to.
433    * @return The parameter value converted to the specified class type.
434    * @throws BadRequest Thrown if input could not be parsed.
435    * @throws InternalServerError Thrown if any other exception occurs.
436    */
437   public <T> T get(String name, T def, Type type, Type...args) throws BadRequest, InternalServerError {
438      return getInner(null, null, name, def, (ClassMeta<T>)getClassMeta(type, args));
439   }
440
441   /**
442    * Same as {@link #get(String, Object, Type, Type...)} but allows you to override the part parser.
443    *
444    * @param parser
445    *    The parser to use for parsing the string value.
446    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
447    * @param schema
448    *    The schema object that defines the format of the input.
449    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
450    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
451    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
452    * @param name The parameter name.
453    * @param type
454    *    The type of object to create.
455    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
456    * @param args
457    *    The type arguments of the class if it's a collection or map.
458    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
459    *    <br>Ignored if the main type is not a map or collection.
460    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
461    * @param <T> The class type to convert the parameter value to.
462    * @return The parameter value converted to the specified class type.
463    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
464    * @throws InternalServerError Thrown if any other exception occurs.
465    */
466   public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws BadRequest, InternalServerError {
467      return getInner(parser, schema, name, def, (ClassMeta<T>)getClassMeta(type, args));
468   }
469
470   /**
471    * Same as {@link #get(String, Class)} except for use on multi-part parameters
472    * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of <js>"&amp;key=@(1,2,3)"</js>).
473    *
474    * <p>
475    * This method must only be called when parsing into classes of type Collection or array.
476    *
477    * @param name The query parameter name.
478    * @param c The class type to convert the parameter value to.
479    * @param <T> The class type to convert the parameter value to.
480    * @return The query parameter value converted to the specified class type.
481    * @throws BadRequest Thrown if input could not be parsed.
482    * @throws InternalServerError Thrown if any other exception occurs.
483    */
484   public <T> T getAll(String name, Class<T> c) throws BadRequest, InternalServerError {
485      return getAllInner(null, null, name, getClassMeta(c));
486   }
487
488   /**
489    * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
490    * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of <js>"&amp;key=@(1,2,3)"</js>).
491    *
492    * <p>
493    * This method must only be called when parsing into classes of type Collection or array.
494    *
495    * @param name The query parameter name.
496    * @param type
497    *    The type of object to create.
498    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
499    * @param args
500    *    The type arguments of the class if it's a collection or map.
501    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
502    *    <br>Ignored if the main type is not a map or collection.
503    * @param <T> The class type to convert the parameter value to.
504    * @return The query parameter value converted to the specified class type.
505    * @throws BadRequest Thrown if input could not be parsed.
506    * @throws InternalServerError Thrown if any other exception occurs.
507    */
508   public <T> T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError {
509      return getAllInner(null, null, name, (ClassMeta<T>)getClassMeta(type, args));
510   }
511
512   /**
513    * Same as {@link #getAll(String, Type, Type...)} but allows you to override the part parser.
514    *
515    * @param parser
516    *    The parser to use for parsing the string value.
517    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
518    * @param schema
519    *    The schema object that defines the format of the input.
520    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
521    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
522    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
523    * @param name The query parameter name.
524    * @param type
525    *    The type of object to create.
526    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
527    * @param args
528    *    The type arguments of the class if it's a collection or map.
529    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
530    *    <br>Ignored if the main type is not a map or collection.
531    * @param <T> The class type to convert the parameter value to.
532    * @return The query parameter value converted to the specified class type.
533    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
534    * @throws InternalServerError Thrown if any other exception occurs.
535    */
536   public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
537      return getAllInner(parser, schema, name, getClassMeta(type, args));
538   }
539
540   /**
541    * Returns <jk>true</jk> if the request contains any of the specified query parameters.
542    *
543    * @param params The list of parameters to check for.
544    * @return <jk>true</jk> if the request contains any of the specified query parameters.
545    */
546   public boolean containsAnyKeys(String...params) {
547      for (String p : params)
548         if (containsKey(p))
549            return true;
550      return false;
551   }
552
553   /**
554    * Locates the special search query arguments in the query and returns them as a {@link SearchArgs} object.
555    *
556    * <p>
557    * The query arguments are as follows:
558    * <ul class='spaced-list'>
559    *    <li>
560    *       <js>"&amp;s="</js> - A comma-delimited list of column-name/search-token pairs.
561    *       <br>Example: <js>"&amp;s=column1=foo*,column2=*bar"</js>
562    *    <li>
563    *       <js>"&amp;v="</js> - A comma-delimited list column names to view.
564    *       <br>Example: <js>"&amp;v=column1,column2"</js>
565    *    <li>
566    *       <js>"&amp;o="</js> - A comma-delimited list column names to sort by.
567    *       <br>Column names can be suffixed with <js>'-'</js> to indicate descending order.
568    *       <br>Example: <js>"&amp;o=column1,column2-"</js>
569    *    <li>
570    *       <js>"&amp;p="</js> - The zero-index row number of the first row to display.
571    *       <br>Example: <js>"&amp;p=100"</js>
572    *    <li>
573    *       <js>"&amp;l="</js> - The number of rows to return.
574    *       <br><c>0</c> implies return all rows.
575    *       <br>Example: <js>"&amp;l=100"</js>
576    *    <li>
577    *       <js>"&amp;i="</js> - The case-insensitive search flag.
578    *       <br>Example: <js>"&amp;i=true"</js>
579    * </ul>
580    *
581    * <ul class='notes'>
582    *    <li>
583    *       Whitespace is trimmed in the parameters.
584    * </ul>
585    *
586    * @return
587    *    A new {@link SearchArgs} object initialized with the special search query arguments.
588    *    <br>Returns <jk>null</jk> if no search arguments were found.
589    */
590   public SearchArgs getSearchArgs() {
591      if (hasAny("s","v","o","p","l","i")) {
592         return new SearchArgs.Builder()
593            .search(getString("s"))
594            .view(getString("v"))
595            .sort(getString("o"))
596            .position(getInt("p"))
597            .limit(getInt("l"))
598            .ignoreCase(getBoolean("i"))
599            .build();
600      }
601      return null;
602   }
603
604   /**
605    * Returns <jk>true</jk> if the query parameters contains any of the specified names.
606    *
607    * @param paramNames The parameter names to check for.
608    * @return <jk>true</jk> if the query parameters contains any of the specified names.
609    */
610   public boolean hasAny(String...paramNames) {
611      for (String p : paramNames)
612         if (containsKey(p))
613            return true;
614      return false;
615   }
616
617   /* Workhorse method */
618   private <T> T getInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
619      if (parser == null)
620         parser = req.getPartParser();
621      try {
622         if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
623            OMap m = new OMap();
624            for (Map.Entry<String,String[]> e : this.entrySet()) {
625               String k = e.getKey();
626               HttpPartSchema pschema = schema == null ? null : schema.getProperty(k);
627               ClassMeta<?> cm2 = cm.getValueType();
628               if (cm.getValueType().isCollectionOrArray())
629                  m.put(k, getAllInner(parser, pschema, k, cm2));
630               else
631                  m.put(k, getInner(parser, pschema, k, null, cm2));
632            }
633            return req.getBeanSession().convertToType(m, cm);
634         }
635         T t = parse(parser, schema, getString(name), cm);
636         return (t == null ? def : t);
637      } catch (SchemaValidationException e) {
638         throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name);
639      } catch (ParseException e) {
640         throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ;
641      } catch (Exception e) {
642         throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ;
643      }
644   }
645
646   /* Workhorse method */
647   @SuppressWarnings("rawtypes")
648   private <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, ClassMeta<T> cm) throws BadRequest, InternalServerError {
649      String[] p = get(name);
650      if (schema == null)
651         schema = HttpPartSchema.DEFAULT;
652      try {
653         if (cm.isArray()) {
654            List c = new ArrayList();
655            for (int i = 0; i < p.length; i++)
656               c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
657            return (T)toArray(c, cm.getElementType().getInnerClass());
658         } else if (cm.isCollection()) {
659            Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new OList());
660            for (int i = 0; i < p.length; i++)
661               c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
662            return (T)c;
663         }
664      } catch (SchemaValidationException e) {
665         throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name);
666      } catch (ParseException e) {
667         throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ;
668      } catch (Exception e) {
669         throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ;
670      }
671      throw new InternalServerError("Invalid call to getParameters(String, ClassMeta).  Class type must be a Collection or array.");
672   }
673
674   private <T> T parse(HttpPartParserSession parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws SchemaValidationException, ParseException {
675      if (parser == null)
676         parser = this.parser;
677      return parser.parse(HttpPartType.QUERY, schema, val, c);
678   }
679
680   /**
681    * Converts the query parameters to a readable string.
682    *
683    * @param sorted Sort the query parameters by name.
684    * @return A JSON string containing the contents of the query parameters.
685    */
686   public String toString(boolean sorted) {
687      Map<String,Object> m = null;
688      if (sorted)
689         m = new TreeMap<>();
690      else
691         m = new LinkedHashMap<>();
692      for (Map.Entry<String,String[]> e : this.entrySet()) {
693         String[] v = e.getValue();
694         m.put(e.getKey(), v.length == 1 ? v[0] : v);
695      }
696      return SimpleJsonSerializer.DEFAULT.toString(m);
697   }
698
699   /**
700    * Converts this object to a query string.
701    *
702    * <p>
703    * Returned query string does not start with <js>'?'</js>.
704    *
705    * @return A new query string, or an empty string if this object is empty.
706    */
707   public String asQueryString() {
708      StringBuilder sb = new StringBuilder();
709      for (Map.Entry<String,String[]> e : this.entrySet()) {
710         for (int i = 0; i < e.getValue().length; i++) {
711            if (sb.length() > 0)
712               sb.append("&");
713            sb.append(urlEncode(e.getKey())).append('=').append(urlEncode(e.getValue()[i]));
714         }
715      }
716      return sb.toString();
717   }
718
719   @Override /* Object */
720   public String toString() {
721      return toString(false);
722   }
723
724   //-----------------------------------------------------------------------------------------------------------------
725   // Helper methods
726   //-----------------------------------------------------------------------------------------------------------------
727
728   private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
729      return req.getBeanSession().getClassMeta(type, args);
730   }
731
732   private <T> ClassMeta<T> getClassMeta(Class<T> type) {
733      return req.getBeanSession().getClassMeta(type);
734   }
735}