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