001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest;
014
015import static org.apache.juneau.internal.ArrayUtils.*;
016import static org.apache.juneau.internal.StringUtils.*;
017
018import java.lang.reflect.*;
019import java.util.*;
020
021import javax.servlet.http.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.httppart.*;
025import org.apache.juneau.internal.*;
026import org.apache.juneau.json.*;
027import org.apache.juneau.oapi.*;
028import org.apache.juneau.parser.*;
029import org.apache.juneau.rest.exception.*;
030import org.apache.juneau.utils.*;
031
032/**
033 * Represents the query parameters in an HTTP request.
034 *
035 * <p>
036 * Similar in functionality to the {@link HttpServletRequest#getParameter(String)} except only looks in the URL string, not parameters from
037 * URL-Encoded FORM posts.
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 * <h5 class='section'>See Also:</h5>
041 * <ul>
042 *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestQuery}
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 HttpPartParser parser;
051
052   RequestQuery(RestRequest req) {
053      this.req = req;
054   }
055
056   RequestQuery parser(HttpPartParser 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, asStrings(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 <code>toString()</code>.
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    * Sets a request query parameter value.
114    *
115    * <p>
116    * This overwrites any existing value.
117    *
118    * @param name The parameter name.
119    * @param value
120    *    The parameter value.
121    *    <br>Can be <jk>null</jk>.
122    */
123   public void put(String name, Object value) {
124      if (value == null)
125         put(name, null);
126      else
127         put(name, asStrings(value));
128   }
129
130   /**
131    * Returns a query parameter value as a string.
132    *
133    * <p>
134    * If multiple query parameters have the same name, this returns only the first instance.
135    *
136    * @param name The URL parameter name.
137    * @return
138    *    The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
139    */
140   public String getString(String name) {
141      String[] v = get(name);
142      if (v == null || v.length == 0)
143         return null;
144
145      // Fix for behavior difference between Tomcat and WAS.
146      // getParameter("foo") on "&foo" in Tomcat returns "".
147      // getParameter("foo") on "&foo" in WAS returns null.
148      if (v.length == 1 && v[0] == null)
149         return "";
150
151      return v[0];
152   }
153
154   /**
155    * Same as {@link #getString(String)} but returns the specified default value if the query parameter was not
156    * specified.
157    *
158    * @param name The URL parameter name.
159    * @param def The default value.
160    * @return
161    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
162    */
163   public String getString(String name, String def) {
164      String s = getString(name);
165      return StringUtils.isEmpty(s) ? def : s;
166   }
167
168   /**
169    * Same as {@link #getString(String)} but converts the value to an integer.
170    *
171    * @param name The URL parameter name.
172    * @return
173    *    The parameter value, or <code>0</code> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
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 URL parameter name.
183    * @param def The default value.
184    * @return
185    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
186    */
187   public int getInt(String name, int def) {
188      String s = getString(name);
189      return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
190   }
191
192   /**
193    * Same as {@link #getString(String)} but converts the value to a boolean.
194    *
195    * @param name The URL parameter name.
196    * @return
197    *    The parameter value, or <jk>false</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
198    */
199   public boolean getBoolean(String name) {
200      return getBoolean(name, false);
201   }
202
203   /**
204    * Same as {@link #getString(String,String)} but converts the value to a boolean.
205    *
206    * @param name The URL parameter name.
207    * @param def The default value.
208    * @return
209    *    The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>).
210    */
211   public boolean getBoolean(String name, boolean def) {
212      String s = getString(name);
213      return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
214   }
215
216   /**
217    * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
218    *
219    * <h5 class='section'>Examples:</h5>
220    * <p class='bcode w800'>
221    *    <jc>// Parse into an integer.</jc>
222    *    <jk>int</jk> myparam = query.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
223    *
224    *    <jc>// Parse into an int array.</jc>
225    *    <jk>int</jk>[] myparam = query.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
226
227    *    <jc>// Parse into a bean.</jc>
228    *    MyBean myparam = query.get(<js>"myparam"</js>, MyBean.<jk>class</jk>);
229    *
230    *    <jc>// Parse into a linked-list of objects.</jc>
231    *    List myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
232    *
233    *    <jc>// Parse into a map of object keys/values.</jc>
234    *    Map myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
235    * </p>
236    *
237    * <h5 class='section'>See Also:</h5>
238    * <ul>
239    *    <li class='jf'>{@link RestContext#REST_partParser}
240    * </ul>
241    *
242    * @param name The parameter name.
243    * @param type The class type to convert the parameter value to.
244    * @param <T> The class type to convert the parameter value to.
245    * @return The parameter value converted to the specified class type.
246    * @throws BadRequest Thrown if input could not be parsed.
247    * @throws InternalServerError Thrown if any other exception occurs.
248    */
249   public <T> T get(String name, Class<T> type) throws BadRequest, InternalServerError {
250      return getInner(null, null, name, null, getClassMeta(type));
251   }
252
253   /**
254    * Same as {@link #get(String, Class)} but allows you to override the part parser.
255    *
256    * @param parser
257    *    The parser to use for parsing the string value.
258    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
259    * @param schema
260    *    The schema object that defines the format of the input.
261    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
262    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
263    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
264    * @param name The parameter name.
265    * @param type The class type to convert the parameter value to.
266    * @param <T> The class type to convert the parameter value to.
267    * @return The parameter value converted to the specified class type.
268    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
269    * @throws InternalServerError Thrown if any other exception occurs.
270    */
271   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
272      return getInner(parser, schema, name, null, getClassMeta(type));
273   }
274
275   /**
276    * Same as {@link #get(String, Class)} except returns a default value if not found.
277    *
278    * @param name The parameter name.
279    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
280    * @param type The class type to convert the parameter value to.
281    * @param <T> The class type to convert the parameter value to.
282    * @return The parameter value converted to the specified class type.
283    * @throws BadRequest Thrown if input could not be parsed.
284    * @throws InternalServerError Thrown if any other exception occurs.
285    */
286   public <T> T get(String name, T def, Class<T> type) throws BadRequest, InternalServerError {
287      return getInner(null, null, name, def, getClassMeta(type));
288   }
289
290   /**
291    * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
292    *
293    * @param parser
294    *    The parser to use for parsing the string value.
295    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
296    * @param schema
297    *    The schema object that defines the format of the input.
298    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
299    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
300    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
301    * @param name The parameter name.
302    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
303    * @param type The class type to convert the parameter value to.
304    * @param <T> The class type to convert the parameter value to.
305    * @return The parameter value converted to the specified class type.
306    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
307    * @throws InternalServerError Thrown if any other exception occurs.
308    */
309   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
310      return getInner(parser, schema, name, def, getClassMeta(type));
311   }
312
313   /**
314    * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
315    *
316    * <p>
317    * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created.
318    *
319    * <h5 class='section'>Examples:</h5>
320    * <p class='bcode w800'>
321    *    <jc>// Parse into a linked-list of strings.</jc>
322    *    List&lt;String&gt; myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
323    *
324    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
325    *    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>);
326    *
327    *    <jc>// Parse into a map of string keys/values.</jc>
328    *    Map&lt;String,String&gt; myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
329    *
330    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
331    *    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>);
332    * </p>
333    *
334    * <h5 class='section'>Notes:</h5>
335    * <ul class='spaced-list'>
336    *    <li>
337    *       <code>Collections</code> must be followed by zero or one parameter representing the value type.
338    *    <li>
339    *       <code>Maps</code> must be followed by zero or two parameters representing the key and value types.
340    * </ul>
341    *
342    * <h5 class='section'>See Also:</h5>
343    * <ul>
344    *    <li class='jf'>{@link RestContext#REST_partParser}
345    * </ul>
346    *
347    * @param name The parameter name.
348    * @param type
349    *    The type of object to create.
350    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
351    * @param args
352    *    The type arguments of the class if it's a collection or map.
353    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
354    *    <br>Ignored if the main type is not a map or collection.
355    * @param <T> The class type to convert the parameter value to.
356    * @return The parameter value converted to the specified class type.
357    * @throws BadRequest Thrown if input could not be parsed.
358    * @throws InternalServerError Thrown if any other exception occurs.
359    */
360   public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError {
361      return getInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
362   }
363
364   /**
365    * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser.
366    *
367    * @param parser
368    *    The parser to use for parsing the string value.
369    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
370    * @param schema
371    *    The schema object that defines the format of the input.
372    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
373    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
374    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
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 or fails schema validation.
386    * @throws InternalServerError Thrown if any other exception occurs.
387    */
388   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
389      return getInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
390   }
391
392   /**
393    * Same as {@link #get(String, Class)} except returns a default value if not found.
394    *
395    * @param name The parameter name.
396    * @param type
397    *    The type of object to create.
398    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
399    * @param args
400    *    The type arguments of the class if it's a collection or map.
401    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
402    *    <br>Ignored if the main type is not a map or collection.
403    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
404    * @param <T> The class type to convert the parameter value to.
405    * @return The parameter value converted to the specified class type.
406    * @throws BadRequest Thrown if input could not be parsed.
407    * @throws InternalServerError Thrown if any other exception occurs.
408    */
409   public <T> T get(String name, T def, Type type, Type...args) throws BadRequest, InternalServerError {
410      return getInner(null, null, name, def, (ClassMeta<T>)getClassMeta(type, args));
411   }
412
413   /**
414    * Same as {@link #get(String, Object, Type, Type...)} but allows you to override the part parser.
415    *
416    * @param parser
417    *    The parser to use for parsing the string value.
418    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
419    * @param schema
420    *    The schema object that defines the format of the input.
421    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
422    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
423    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
424    * @param name The parameter name.
425    * @param type
426    *    The type of object to create.
427    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
428    * @param args
429    *    The type arguments of the class if it's a collection or map.
430    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
431    *    <br>Ignored if the main type is not a map or collection.
432    * @param def The default value if the parameter was not specified or is <jk>null</jk>.
433    * @param <T> The class type to convert the parameter value to.
434    * @return The parameter value converted to the specified class type.
435    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
436    * @throws InternalServerError Thrown if any other exception occurs.
437    */
438   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws BadRequest, InternalServerError {
439      return getInner(parser, schema, name, def, (ClassMeta<T>)getClassMeta(type, args));
440   }
441
442   /**
443    * Same as {@link #get(String, Class)} except for use on multi-part parameters
444    * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of <js>"&amp;key=@(1,2,3)"</js>).
445    *
446    * <p>
447    * This method must only be called when parsing into classes of type Collection or array.
448    *
449    * @param name The query parameter name.
450    * @param c The class type to convert the parameter value to.
451    * @param <T> The class type to convert the parameter value to.
452    * @return The query parameter value converted to the specified class type.
453    * @throws BadRequest Thrown if input could not be parsed.
454    * @throws InternalServerError Thrown if any other exception occurs.
455    */
456   public <T> T getAll(String name, Class<T> c) throws BadRequest, InternalServerError {
457      return getAllInner(null, null, name, null, getClassMeta(c));
458   }
459
460   /**
461    * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
462    * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of <js>"&amp;key=@(1,2,3)"</js>).
463    *
464    * <p>
465    * This method must only be called when parsing into classes of type Collection or array.
466    *
467    * @param name The query parameter name.
468    * @param type
469    *    The type of object to create.
470    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
471    * @param args
472    *    The type arguments of the class if it's a collection or map.
473    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
474    *    <br>Ignored if the main type is not a map or collection.
475    * @param <T> The class type to convert the parameter value to.
476    * @return The query parameter value converted to the specified class type.
477    * @throws BadRequest Thrown if input could not be parsed.
478    * @throws InternalServerError Thrown if any other exception occurs.
479    */
480   public <T> T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError {
481      return getAllInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
482   }
483
484   /**
485    * Same as {@link #getAll(String, Type, Type...)} but allows you to override the part parser.
486    *
487    * @param parser
488    *    The parser to use for parsing the string value.
489    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
490    * @param schema
491    *    The schema object that defines the format of the input.
492    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
493    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
494    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
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 or fails schema validation.
506    * @throws InternalServerError Thrown if any other exception occurs.
507    */
508   public <T> T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
509      return getAllInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
510   }
511
512   /**
513    * Returns <jk>true</jk> if the request contains any of the specified query parameters.
514    *
515    * @param params The list of parameters to check for.
516    * @return <jk>true</jk> if the request contains any of the specified query parameters.
517    */
518   public boolean containsAnyKeys(String...params) {
519      for (String p : params)
520         if (containsKey(p))
521            return true;
522      return false;
523   }
524
525   /**
526    * Locates the special search query arguments in the query and returns them as a {@link SearchArgs} object.
527    *
528    * <p>
529    * The query arguments are as follows:
530    * <ul class='spaced-list'>
531    *    <li>
532    *       <js>"&amp;s="</js> - A comma-delimited list of column-name/search-token pairs.
533    *       <br>Example: <js>"&amp;s=column1=foo*,column2=*bar"</js>
534    *    <li>
535    *       <js>"&amp;v="</js> - A comma-delimited list column names to view.
536    *       <br>Example: <js>"&amp;v=column1,column2"</js>
537    *    <li>
538    *       <js>"&amp;o="</js> - A comma-delimited list column names to sort by.
539    *       <br>Column names can be suffixed with <js>'-'</js> to indicate descending order.
540    *       <br>Example: <js>"&amp;o=column1,column2-"</js>
541    *    <li>
542    *       <js>"&amp;p="</js> - The zero-index row number of the first row to display.
543    *       <br>Example: <js>"&amp;p=100"</js>
544    *    <li>
545    *       <js>"&amp;l="</js> - The number of rows to return.
546    *       <br><code>0</code> implies return all rows.
547    *       <br>Example: <js>"&amp;l=100"</js>
548    *    <li>
549    *       <js>"&amp;i="</js> - The case-insensitive search flag.
550    *       <br>Example: <js>"&amp;i=true"</js>
551    * </ul>
552    *
553    * <h5 class='section'>Notes:</h5>
554    * <ul class='spaced-list'>
555    *    <li>
556    *       Whitespace is trimmed in the parameters.
557    * </ul>
558    *
559    * @return
560    *    A new {@link SearchArgs} object initialized with the special search query arguments.
561    *    <br>Returns <jk>null</jk> if no search arguments were found.
562    */
563   public SearchArgs getSearchArgs() {
564      if (hasAny("s","v","o","p","l","i")) {
565         return new SearchArgs.Builder()
566            .search(getString("s"))
567            .view(getString("v"))
568            .sort(getString("o"))
569            .position(getInt("p"))
570            .limit(getInt("l"))
571            .ignoreCase(getBoolean("i"))
572            .build();
573      }
574      return null;
575   }
576
577   /**
578    * Returns <jk>true</jk> if the query parameters contains any of the specified names.
579    *
580    * @param paramNames The parameter names to check for.
581    * @return <jk>true</jk> if the query parameters contains any of the specified names.
582    */
583   public boolean hasAny(String...paramNames) {
584      for (String p : paramNames)
585         if (containsKey(p))
586            return true;
587      return false;
588   }
589
590   /* Workhorse method */
591   private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
592      try {
593         if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
594            ObjectMap m = new ObjectMap();
595            for (Map.Entry<String,String[]> e : this.entrySet()) {
596               String k = e.getKey();
597               HttpPartSchema pschema = schema == null ? null : schema.getProperty(k);
598               ClassMeta<?> cm2 = cm.getValueType();
599               if (cm.getValueType().isCollectionOrArray())
600                  m.put(k, getAllInner(parser, pschema, k, null, cm2));
601               else
602                  m.put(k, getInner(parser, pschema, k, null, cm2));
603            }
604            return req.getBeanSession().convertToType(m, cm);
605         }
606         T t = parse(parser, schema, getString(name), cm);
607         return (t == null ? def : t);
608      } catch (SchemaValidationException e) {
609         throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name);
610      } catch (ParseException e) {
611         throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ;
612      } catch (Exception e) {
613         throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ;
614      }
615   }
616
617   /* Workhorse method */
618   @SuppressWarnings("rawtypes")
619   private <T> T getAllInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
620      String[] p = get(name);
621      if (p == null)
622         return def;
623      if (schema == null)
624         schema = HttpPartSchema.DEFAULT;
625      try {
626         if (cm.isArray()) {
627            List c = new ArrayList();
628            for (int i = 0; i < p.length; i++)
629               c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
630            return (T)toArray(c, cm.getElementType().getInnerClass());
631         } else if (cm.isCollection()) {
632            Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
633            for (int i = 0; i < p.length; i++)
634               c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
635            return (T)c;
636         }
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      throw new InternalServerError("Invalid call to getParameters(String, ClassMeta).  Class type must be a Collection or array.");
645   }
646
647   private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws SchemaValidationException, ParseException {
648      if (parser == null)
649         parser = this.parser;
650      return parser.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.QUERY, schema, val, c);
651   }
652
653   /**
654    * Converts the query parameters to a readable string.
655    *
656    * @param sorted Sort the query parameters by name.
657    * @return A JSON string containing the contents of the query parameters.
658    */
659   public String toString(boolean sorted) {
660      Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : new LinkedHashMap<String,Object>());
661      for (Map.Entry<String,String[]> e : this.entrySet()) {
662         String[] v = e.getValue();
663         m.put(e.getKey(), v.length == 1 ? v[0] : v);
664      }
665      return SimpleJsonSerializer.DEFAULT.toString(m);
666   }
667
668   /**
669    * Converts this object to a query string.
670    *
671    * <p>
672    * Returned query string does not start with <js>'?'</js>.
673    *
674    * @return A new query string, or an empty string if this object is empty.
675    */
676   public String toQueryString() {
677      StringBuilder sb = new StringBuilder();
678      for (Map.Entry<String,String[]> e : this.entrySet()) {
679         for (int i = 0; i < e.getValue().length; i++) {
680            if (sb.length() > 0)
681               sb.append("&");
682            sb.append(urlEncode(e.getKey())).append('=').append(urlEncode(e.getValue()[i]));
683         }
684      }
685      return sb.toString();
686   }
687
688   @Override /* Object */
689   public String toString() {
690      return toString(false);
691   }
692
693   //-----------------------------------------------------------------------------------------------------------------
694   // Helper methods
695   //-----------------------------------------------------------------------------------------------------------------
696
697   private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
698      return req.getBeanSession().getClassMeta(type, args);
699   }
700
701   private <T> ClassMeta<T> getClassMeta(Class<T> type) {
702      return req.getBeanSession().getClassMeta(type);
703   }
704
705   /**
706    * @deprecated Use {@link #get(HttpPartParser, HttpPartSchema, String, Class)}
707    */
708   @SuppressWarnings({ "javadoc", "unused" })
709   @Deprecated
710   public <T> T get(HttpPartParser parser, String name, Class<T> type) throws ParseException {
711      return get(parser, null, name, type);
712   }
713
714   /**
715    * @deprecated Use {@link #get(HttpPartParser, HttpPartSchema, String, Class)}
716    */
717   @SuppressWarnings({ "javadoc", "unused" })
718   @Deprecated
719   public <T> T get(HttpPartParser parser, String name, T def, Class<T> type) throws ParseException {
720      return get(parser, null, name, def, type);
721   }
722
723   /**
724    * @deprecated Use {@link #get(HttpPartParser, HttpPartSchema, String, Type, Type...)}
725    */
726   @SuppressWarnings({ "javadoc", "unused" })
727   @Deprecated
728   public <T> T get(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
729      return get(parser, (HttpPartSchema)null, name, type, args);
730   }
731
732   /**
733    * @deprecated Use {@link #get(HttpPartParser, HttpPartSchema, String, Object, Type, Type...)}
734    */
735   @SuppressWarnings({ "javadoc", "unused" })
736   @Deprecated
737   public <T> T get(HttpPartParser parser, String name, Object def, Type type, Type...args) throws ParseException {
738      return (T)get(parser, null, name, def, type, args);
739   }
740
741   /**
742    * @deprecated Use {@link #getAll(HttpPartParser, HttpPartSchema, String, Type, Type...)}
743    */
744   @SuppressWarnings({ "javadoc", "unused" })
745   @Deprecated
746   public <T> T getAll(HttpPartParser parser, String name, Type type, Type...args) throws ParseException {
747      return getAll(parser, null, name, type, args);
748   }
749
750   /**
751    * @deprecated Unused.
752    */
753   @Deprecated
754   public RequestQuery() {
755      this(null);
756   }
757}