| // *************************************************************************************************************************** |
| // * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * |
| // * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * |
| // * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * |
| // * with the License. You may obtain a copy of the License at * |
| // * * |
| // * http://www.apache.org/licenses/LICENSE-2.0 * |
| // * * |
| // * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * |
| // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * |
| // * specific language governing permissions and limitations under the License. * |
| // *************************************************************************************************************************** |
| package org.apache.juneau.rest; |
| |
| import static org.apache.juneau.internal.ArrayUtils.*; |
| import static org.apache.juneau.internal.StringUtils.*; |
| |
| import java.lang.reflect.*; |
| import java.util.*; |
| |
| import javax.servlet.http.*; |
| |
| import org.apache.juneau.*; |
| import org.apache.juneau.httppart.*; |
| import org.apache.juneau.internal.*; |
| import org.apache.juneau.json.*; |
| import org.apache.juneau.oapi.*; |
| import org.apache.juneau.parser.*; |
| import org.apache.juneau.http.exception.*; |
| import org.apache.juneau.utils.*; |
| |
| /** |
| * Represents the query parameters in an HTTP request. |
| * |
| * <p> |
| * Similar in functionality to the {@link HttpServletRequest#getParameter(String)} except only looks in the URL string, not parameters from |
| * URL-Encoded FORM posts. |
| * <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. |
| * |
| * <ul class='seealso'> |
| * <li class='link'>{@doc juneau-rest-server.RestMethod.RequestQuery} |
| * </ul> |
| */ |
| @SuppressWarnings("unchecked") |
| public final class RequestQuery extends LinkedHashMap<String,String[]> { |
| private static final long serialVersionUID = 1L; |
| |
| private final RestRequest req; |
| private HttpPartParser parser; |
| |
| RequestQuery(RestRequest req) { |
| this.req = req; |
| } |
| |
| RequestQuery parser(HttpPartParser parser) { |
| this.parser = parser; |
| return this; |
| } |
| |
| /* |
| * Create a copy of the request query parameters. |
| */ |
| RequestQuery copy() { |
| RequestQuery rq = new RequestQuery(req); |
| rq.putAll(this); |
| return rq; |
| } |
| |
| /** |
| * Adds default entries to these query parameters. |
| * |
| * <p> |
| * This includes the default queries defined at the resource and method levels. |
| * |
| * @param defaultEntries |
| * The default entries. |
| * <br>Can be <jk>null</jk>. |
| * @return This object (for method chaining). |
| */ |
| public RequestQuery addDefault(Map<String,Object> defaultEntries) { |
| if (defaultEntries != null) { |
| for (Map.Entry<String,Object> e : defaultEntries.entrySet()) { |
| String key = e.getKey(); |
| Object value = e.getValue(); |
| String[] v = get(key); |
| if (v == null || v.length == 0 || StringUtils.isEmpty(v[0])) |
| put(key, stringifyAll(value)); |
| } |
| } |
| return this; |
| } |
| |
| /** |
| * Adds a default entries to these query parameters. |
| * |
| * <p> |
| * Similar to {@link #put(String, Object)} but doesn't override existing values. |
| * |
| * @param name |
| * The query parameter name. |
| * @param value |
| * The query parameter value. |
| * <br>Converted to a String using <c>toString()</c>. |
| * <br>Ignored if value is <jk>null</jk> or blank. |
| * @return This object (for method chaining). |
| */ |
| public RequestQuery addDefault(String name, Object value) { |
| return addDefault(Collections.singletonMap(name, value)); |
| } |
| |
| /** |
| * Same as {@link #get(Object)} but allows you to find the query parameter using a case-insensitive match. |
| * |
| * @param name The query parameter name. |
| * @param caseInsensitive If <jk>true</jk> use case-insensitive matching on the query parameter name. |
| * @return The resolved entry, or <jk>null</jk> if not found. |
| */ |
| public String[] get(String name, boolean caseInsensitive) { |
| if (! caseInsensitive) |
| return get(name); |
| for (Map.Entry<String,String[]> e : entrySet()) |
| if (e.getKey().equalsIgnoreCase(name)) |
| return e.getValue(); |
| return null; |
| } |
| |
| /** |
| * Sets a request query parameter value. |
| * |
| * <p> |
| * This overwrites any existing value. |
| * |
| * @param name The parameter name. |
| * @param value |
| * The parameter value. |
| * <br>Can be <jk>null</jk>. |
| */ |
| public void put(String name, Object value) { |
| if (value == null) |
| put(name, null); |
| else |
| put(name, stringifyAll(value)); |
| } |
| |
| /** |
| * Returns a query parameter value as a string. |
| * |
| * <p> |
| * If multiple query parameters have the same name, this returns only the first instance. |
| * |
| * @param name The URL parameter name. |
| * @return |
| * The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public String getString(String name) { |
| return getString(name, false); |
| } |
| |
| /** |
| * Same as {@link #getString(String)} but allows you to search for the query parameter using case-insensitive matching. |
| * |
| * <p> |
| * If multiple query parameters have the same name, this returns only the first instance. |
| * |
| * @param name The URL parameter name. |
| * @param caseInsensitive If <jk>true</jk> use case insensitive matching on the query parameter name. |
| * @return |
| * The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public String getString(String name, boolean caseInsensitive) { |
| String[] v = get(name, caseInsensitive); |
| if (v == null || v.length == 0) |
| return null; |
| |
| // Fix for behavior difference between Tomcat and WAS. |
| // getParameter("foo") on "&foo" in Tomcat returns "". |
| // getParameter("foo") on "&foo" in WAS returns null. |
| if (v.length == 1 && v[0] == null) |
| return ""; |
| |
| return v[0]; |
| } |
| |
| /** |
| * Same as {@link #getString(String)} but returns the specified default value if the query parameter was not |
| * specified. |
| * |
| * @param name The URL parameter name. |
| * @param def The default value. |
| * @return |
| * The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public String getString(String name, String def) { |
| String s = getString(name); |
| return StringUtils.isEmpty(s) ? def : s; |
| } |
| |
| /** |
| * Same as {@link #getString(String)} but converts the value to an integer. |
| * |
| * @param name The URL parameter name. |
| * @return |
| * The parameter value, or <c>0</c> if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public int getInt(String name) { |
| return getInt(name, 0); |
| } |
| |
| /** |
| * Same as {@link #getString(String,String)} but converts the value to an integer. |
| * |
| * @param name The URL parameter name. |
| * @param def The default value. |
| * @return |
| * The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public int getInt(String name, int def) { |
| String s = getString(name); |
| return StringUtils.isEmpty(s) ? def : Integer.parseInt(s); |
| } |
| |
| /** |
| * Same as {@link #getString(String)} but converts the value to a boolean. |
| * |
| * @param name The URL parameter name. |
| * @return |
| * The parameter value, or <jk>false</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public boolean getBoolean(String name) { |
| return getBoolean(name, false); |
| } |
| |
| /** |
| * Same as {@link #getString(String,String)} but converts the value to a boolean. |
| * |
| * @param name The URL parameter name. |
| * @param def The default value. |
| * @return |
| * The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>). |
| */ |
| public boolean getBoolean(String name, boolean def) { |
| String s = getString(name); |
| return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s); |
| } |
| |
| /** |
| * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <p class='bcode w800'> |
| * <jc>// Parse into an integer.</jc> |
| * <jk>int</jk> myparam = query.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>); |
| * |
| * <jc>// Parse into an int array.</jc> |
| * <jk>int</jk>[] myparam = query.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>); |
| |
| * <jc>// Parse into a bean.</jc> |
| * MyBean myparam = query.get(<js>"myparam"</js>, MyBean.<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of objects.</jc> |
| * List myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map of object keys/values.</jc> |
| * Map myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>); |
| * </p> |
| * |
| * <ul class='seealso'> |
| * <li class='jf'>{@link RestContext#REST_partParser} |
| * </ul> |
| * |
| * @param name The parameter name. |
| * @param type The class type to convert the parameter value to. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(String name, Class<T> type) throws BadRequest, InternalServerError { |
| return getInner(null, null, name, null, getClassMeta(type)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Class)} but allows you to override the part parser. |
| * |
| * @param parser |
| * The parser to use for parsing the string value. |
| * <br>If <jk>null</jk>, uses the part parser defined on the resource/method. |
| * @param schema |
| * The schema object that defines the format of the input. |
| * <br>If <jk>null</jk>, defaults to the schema defined on the parser. |
| * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. |
| * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}). |
| * @param name The parameter name. |
| * @param type The class type to convert the parameter value to. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed or fails schema validation. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError { |
| return getInner(parser, schema, name, null, getClassMeta(type)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Class)} except returns a default value if not found. |
| * |
| * @param name The parameter name. |
| * @param def The default value if the parameter was not specified or is <jk>null</jk>. |
| * @param type The class type to convert the parameter value to. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(String name, T def, Class<T> type) throws BadRequest, InternalServerError { |
| return getInner(null, null, name, def, getClassMeta(type)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Object, Class)} but allows you to override the part parser. |
| * |
| * @param parser |
| * The parser to use for parsing the string value. |
| * <br>If <jk>null</jk>, uses the part parser defined on the resource/method. |
| * @param schema |
| * The schema object that defines the format of the input. |
| * <br>If <jk>null</jk>, defaults to the schema defined on the parser. |
| * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. |
| * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}). |
| * @param name The parameter name. |
| * @param def The default value if the parameter was not specified or is <jk>null</jk>. |
| * @param type The class type to convert the parameter value to. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed or fails schema validation. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError { |
| return getInner(parser, schema, name, def, getClassMeta(type)); |
| } |
| |
| /** |
| * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource. |
| * |
| * <p> |
| * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <p class='bcode w800'> |
| * <jc>// Parse into a linked-list of strings.</jc> |
| * List<String> myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of linked-lists of strings.</jc> |
| * List<List<String>> myparam = query.get(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map of string keys/values.</jc> |
| * Map<String,String> myparam = query.get(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> |
| * 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>); |
| * </p> |
| * |
| * <ul class='notes'> |
| * <li> |
| * <c>Collections</c> must be followed by zero or one parameter representing the value type. |
| * <li> |
| * <c>Maps</c> must be followed by zero or two parameters representing the key and value types. |
| * </ul> |
| * |
| * <ul class='seealso'> |
| * <li class='jf'>{@link RestContext#REST_partParser} |
| * </ul> |
| * |
| * @param name The parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser. |
| * |
| * @param parser |
| * The parser to use for parsing the string value. |
| * <br>If <jk>null</jk>, uses the part parser defined on the resource/method. |
| * @param schema |
| * The schema object that defines the format of the input. |
| * <br>If <jk>null</jk>, defaults to the schema defined on the parser. |
| * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. |
| * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}). |
| * @param name The parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed or fails schema validation. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Class)} except returns a default value if not found. |
| * |
| * @param name The parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param def The default value if the parameter was not specified or is <jk>null</jk>. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(String name, T def, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getInner(null, null, name, def, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Object, Type, Type...)} but allows you to override the part parser. |
| * |
| * @param parser |
| * The parser to use for parsing the string value. |
| * <br>If <jk>null</jk>, uses the part parser defined on the resource/method. |
| * @param schema |
| * The schema object that defines the format of the input. |
| * <br>If <jk>null</jk>, defaults to the schema defined on the parser. |
| * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. |
| * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}). |
| * @param name The parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param def The default value if the parameter was not specified or is <jk>null</jk>. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed or fails schema validation. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getInner(parser, schema, name, def, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Class)} except for use on multi-part parameters |
| * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=@(1,2,3)"</js>). |
| * |
| * <p> |
| * This method must only be called when parsing into classes of type Collection or array. |
| * |
| * @param name The query parameter name. |
| * @param c The class type to convert the parameter value to. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The query parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T getAll(String name, Class<T> c) throws BadRequest, InternalServerError { |
| return getAllInner(null, null, name, null, getClassMeta(c)); |
| } |
| |
| /** |
| * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters |
| * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=@(1,2,3)"</js>). |
| * |
| * <p> |
| * This method must only be called when parsing into classes of type Collection or array. |
| * |
| * @param name The query parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The query parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getAllInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Same as {@link #getAll(String, Type, Type...)} but allows you to override the part parser. |
| * |
| * @param parser |
| * The parser to use for parsing the string value. |
| * <br>If <jk>null</jk>, uses the part parser defined on the resource/method. |
| * @param schema |
| * The schema object that defines the format of the input. |
| * <br>If <jk>null</jk>, defaults to the schema defined on the parser. |
| * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. |
| * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}). |
| * @param name The query parameter name. |
| * @param type |
| * The type of object to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @param <T> The class type to convert the parameter value to. |
| * @return The query parameter value converted to the specified class type. |
| * @throws BadRequest Thrown if input could not be parsed or fails schema validation. |
| * @throws InternalServerError Thrown if any other exception occurs. |
| */ |
| public <T> T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError { |
| return getAllInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args)); |
| } |
| |
| /** |
| * Returns <jk>true</jk> if the request contains any of the specified query parameters. |
| * |
| * @param params The list of parameters to check for. |
| * @return <jk>true</jk> if the request contains any of the specified query parameters. |
| */ |
| public boolean containsAnyKeys(String...params) { |
| for (String p : params) |
| if (containsKey(p)) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Locates the special search query arguments in the query and returns them as a {@link SearchArgs} object. |
| * |
| * <p> |
| * The query arguments are as follows: |
| * <ul class='spaced-list'> |
| * <li> |
| * <js>"&s="</js> - A comma-delimited list of column-name/search-token pairs. |
| * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js> |
| * <li> |
| * <js>"&v="</js> - A comma-delimited list column names to view. |
| * <br>Example: <js>"&v=column1,column2"</js> |
| * <li> |
| * <js>"&o="</js> - A comma-delimited list column names to sort by. |
| * <br>Column names can be suffixed with <js>'-'</js> to indicate descending order. |
| * <br>Example: <js>"&o=column1,column2-"</js> |
| * <li> |
| * <js>"&p="</js> - The zero-index row number of the first row to display. |
| * <br>Example: <js>"&p=100"</js> |
| * <li> |
| * <js>"&l="</js> - The number of rows to return. |
| * <br><c>0</c> implies return all rows. |
| * <br>Example: <js>"&l=100"</js> |
| * <li> |
| * <js>"&i="</js> - The case-insensitive search flag. |
| * <br>Example: <js>"&i=true"</js> |
| * </ul> |
| * |
| * <ul class='notes'> |
| * <li> |
| * Whitespace is trimmed in the parameters. |
| * </ul> |
| * |
| * @return |
| * A new {@link SearchArgs} object initialized with the special search query arguments. |
| * <br>Returns <jk>null</jk> if no search arguments were found. |
| */ |
| public SearchArgs getSearchArgs() { |
| if (hasAny("s","v","o","p","l","i")) { |
| return new SearchArgs.Builder() |
| .search(getString("s")) |
| .view(getString("v")) |
| .sort(getString("o")) |
| .position(getInt("p")) |
| .limit(getInt("l")) |
| .ignoreCase(getBoolean("i")) |
| .build(); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns <jk>true</jk> if the query parameters contains any of the specified names. |
| * |
| * @param paramNames The parameter names to check for. |
| * @return <jk>true</jk> if the query parameters contains any of the specified names. |
| */ |
| public boolean hasAny(String...paramNames) { |
| for (String p : paramNames) |
| if (containsKey(p)) |
| return true; |
| return false; |
| } |
| |
| /* Workhorse method */ |
| private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError { |
| try { |
| if (cm.isMapOrBean() && isOneOf(name, "*", "")) { |
| ObjectMap m = new ObjectMap(); |
| for (Map.Entry<String,String[]> e : this.entrySet()) { |
| String k = e.getKey(); |
| HttpPartSchema pschema = schema == null ? null : schema.getProperty(k); |
| ClassMeta<?> cm2 = cm.getValueType(); |
| if (cm.getValueType().isCollectionOrArray()) |
| m.put(k, getAllInner(parser, pschema, k, null, cm2)); |
| else |
| m.put(k, getInner(parser, pschema, k, null, cm2)); |
| } |
| return req.getBeanSession().convertToType(m, cm); |
| } |
| T t = parse(parser, schema, getString(name), cm); |
| return (t == null ? def : t); |
| } catch (SchemaValidationException e) { |
| throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name); |
| } catch (ParseException e) { |
| throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ; |
| } catch (Exception e) { |
| throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ; |
| } |
| } |
| |
| /* Workhorse method */ |
| @SuppressWarnings("rawtypes") |
| private <T> T getAllInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError { |
| String[] p = get(name); |
| if (p == null) |
| return def; |
| if (schema == null) |
| schema = HttpPartSchema.DEFAULT; |
| try { |
| if (cm.isArray()) { |
| List c = new ArrayList(); |
| for (int i = 0; i < p.length; i++) |
| c.add(parse(parser, schema.getItems(), p[i], cm.getElementType())); |
| return (T)toArray(c, cm.getElementType().getInnerClass()); |
| } else if (cm.isCollection()) { |
| Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList()); |
| for (int i = 0; i < p.length; i++) |
| c.add(parse(parser, schema.getItems(), p[i], cm.getElementType())); |
| return (T)c; |
| } |
| } catch (SchemaValidationException e) { |
| throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name); |
| } catch (ParseException e) { |
| throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ; |
| } catch (Exception e) { |
| throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ; |
| } |
| throw new InternalServerError("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array."); |
| } |
| |
| private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws SchemaValidationException, ParseException { |
| if (parser == null) |
| parser = this.parser; |
| return parser.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.QUERY, schema, val, c); |
| } |
| |
| /** |
| * Converts the query parameters to a readable string. |
| * |
| * @param sorted Sort the query parameters by name. |
| * @return A JSON string containing the contents of the query parameters. |
| */ |
| public String toString(boolean sorted) { |
| Map<String,Object> m = null; |
| if (sorted) |
| m = new TreeMap<>(); |
| else |
| m = new LinkedHashMap<>(); |
| for (Map.Entry<String,String[]> e : this.entrySet()) { |
| String[] v = e.getValue(); |
| m.put(e.getKey(), v.length == 1 ? v[0] : v); |
| } |
| return SimpleJsonSerializer.DEFAULT.toString(m); |
| } |
| |
| /** |
| * Converts this object to a query string. |
| * |
| * <p> |
| * Returned query string does not start with <js>'?'</js>. |
| * |
| * @return A new query string, or an empty string if this object is empty. |
| */ |
| public String toQueryString() { |
| StringBuilder sb = new StringBuilder(); |
| for (Map.Entry<String,String[]> e : this.entrySet()) { |
| for (int i = 0; i < e.getValue().length; i++) { |
| if (sb.length() > 0) |
| sb.append("&"); |
| sb.append(urlEncode(e.getKey())).append('=').append(urlEncode(e.getValue()[i])); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| @Override /* Object */ |
| public String toString() { |
| return toString(false); |
| } |
| |
| //----------------------------------------------------------------------------------------------------------------- |
| // Helper methods |
| //----------------------------------------------------------------------------------------------------------------- |
| |
| private <T> ClassMeta<T> getClassMeta(Type type, Type...args) { |
| return req.getBeanSession().getClassMeta(type, args); |
| } |
| |
| private <T> ClassMeta<T> getClassMeta(Class<T> type) { |
| return req.getBeanSession().getClassMeta(type); |
| } |
| } |