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>"&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>"&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>"&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>"&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>"&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>"&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<String> 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<List<String>> 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<String,String> 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<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>); 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>"&key=1&key=2&key=3"</js> instead of <js>"&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>"&key=1&key=2&key=3"</js> instead of <js>"&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>"&s="</js> - A comma-delimited list of column-name/search-token pairs. 533 * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js> 534 * <li> 535 * <js>"&v="</js> - A comma-delimited list column names to view. 536 * <br>Example: <js>"&v=column1,column2"</js> 537 * <li> 538 * <js>"&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>"&o=column1,column2-"</js> 541 * <li> 542 * <js>"&p="</js> - The zero-index row number of the first row to display. 543 * <br>Example: <js>"&p=100"</js> 544 * <li> 545 * <js>"&l="</js> - The number of rows to return. 546 * <br><code>0</code> implies return all rows. 547 * <br>Example: <js>"&l=100"</js> 548 * <li> 549 * <js>"&i="</js> - The case-insensitive search flag. 550 * <br>Example: <js>"&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}