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>"&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>"&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>"&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>"&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>"&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>"&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>"&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<String> 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<List<String>> 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<String,String> 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<String,List<MyBean>> 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>"&key=1&key=2&key=3"</js> instead of <js>"&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>"&key=1&key=2&key=3"</js> instead of <js>"&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>"&s="</js> - A comma-delimited list of column-name/search-token pairs. 561 * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js> 562 * <li> 563 * <js>"&v="</js> - A comma-delimited list column names to view. 564 * <br>Example: <js>"&v=column1,column2"</js> 565 * <li> 566 * <js>"&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>"&o=column1,column2-"</js> 569 * <li> 570 * <js>"&p="</js> - The zero-index row number of the first row to display. 571 * <br>Example: <js>"&p=100"</js> 572 * <li> 573 * <js>"&l="</js> - The number of rows to return. 574 * <br><c>0</c> implies return all rows. 575 * <br>Example: <js>"&l=100"</js> 576 * <li> 577 * <js>"&i="</js> - The case-insensitive search flag. 578 * <br>Example: <js>"&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}