org.apache.juneau.rest.RequestFormData Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * 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 java.lang.reflect.*;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.urlencoding.*;
/**
* Represents the parsed form data parameters in an HTTP request.
*/
@SuppressWarnings("unchecked")
public class RequestFormData extends LinkedHashMap {
private static final long serialVersionUID = 1L;
private UrlEncodingParser parser;
private BeanSession beanSession;
RequestFormData setParser(UrlEncodingParser parser) {
this.parser = parser;
return this;
}
RequestFormData setBeanSession(BeanSession beanSession) {
this.beanSession = beanSession;
return this;
}
/**
* Adds default entries to these form-data parameters.
*
*
* This includes the default form-data parameters defined on the servlet and method levels.
*
* @param defaultEntries The default entries. Can be null .
* @return This object (for method chaining).
*/
public RequestFormData addDefault(Map defaultEntries) {
if (defaultEntries != null) {
for (Map.Entry e : defaultEntries.entrySet()) {
String key = e.getKey(), value = e.getValue();
String[] v = get(key);
if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
put(key, new String[]{value});
}
}
return this;
}
/**
* Sets a request form data parameter value.
*
* @param name The parameter name.
* @param value The parameter value.
*/
public void put(String name, Object value) {
super.put(name, new String[]{StringUtils.toString(value)});
}
/**
* Returns a form data parameter value.
*
*
* Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
*
*
Notes:
*
* - Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the
* underlying servlet API.
*
- This method returns the raw unparsed value, and differs from calling
*
getFormDataParameter(name, String.class)
which will convert the value from UON
* notation:
*
* "null" => null
* "'null'" => "null"
* "'foo bar'" => "foo bar"
* "foo~~bar" => "foo~bar"
*
*
*
* @param name The form data parameter name.
* @return The parameter value, or null if parameter does not exist.
*/
public String getString(String name) {
String[] v = get(name);
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)} except returns a default value if null or empty.
*
* @param name The form data parameter name.
* @param def The default value.
* @return The parameter value, or the default value if parameter does not exist or is null or empty.
*/
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 form data parameter name.
* @return The parameter value, or 0
if parameter does not exist or is null or empty.
*/
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 form data parameter name.
* @param def The default value.
* @return The parameter value, or the default value if parameter does not exist or is null or empty.
*/
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 form data parameter name.
* @return The parameter value, or false if parameter does not exist or is null or empty.
*/
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 form data parameter name.
* @param def The default value.
* @return The parameter value, or the default value if parameter does not exist or is null or empty.
*/
public boolean getBoolean(String name, boolean def) {
String s = getString(name);
return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
}
/**
* Returns the specified form data parameter value converted to a POJO using the {@link UrlEncodingParser}
* registered with this servlet.
*
* Examples:
*
* // Parse into an integer.
* int myparam = req.getFormDataParameter("myparam" , int .class );
*
* // Parse into an int array.
* int [] myparam = req.getFormDataParameter("myparam" , int [].class );
* // Parse into a bean.
* MyBean myparam = req.getFormDataParameter("myparam" , MyBean.class );
*
* // Parse into a linked-list of objects.
* List myparam = req.getFormDataParameter("myparam" , LinkedList.class );
*
* // Parse into a map of object keys/values.
* Map myparam = req.getFormDataParameter("myparam" , TreeMap.class );
*
*
* Notes:
*
* - Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the
* underlying servlet API.
*
*
* @param name The parameter name.
* @param type The class type to convert the parameter value to.
* @param The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
public T get(String name, Class type) throws ParseException {
return parse(name, beanSession.getClassMeta(type));
}
/**
* Same as {@link #get(String, Class)} except returns a default value if not specified.
*
* @param name The parameter name.
* @param def The default value if the parameter was not specified or is null .
* @param type The class type to convert the parameter value to.
* @param The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
public T get(String name, T def, Class type) throws ParseException {
return parse(name, def, beanSession.getClassMeta(type));
}
/**
* Same as {@link #get(String, Class)} except for use on multi-part parameters
* (e.g. "key=1&key=2&key=3" instead of "key=(1,2,3)" )
*
*
* This method must only be called when parsing into classes of type Collection or array.
*
* @param name The parameter name.
* @param type The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
public T getAll(String name, Class type) throws ParseException {
return parseAll(name, beanSession.getClassMeta(type));
}
/**
* Returns the specified form data parameter value converted to a POJO using the {@link UrlEncodingParser}
* registered with this servlet.
*
* Notes:
*
* - Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the
* underlying servlet API.
*
- Use this method if you want to parse into a parameterized
Map
/Collection
object.
*
*
* Examples:
*
* // Parse into a linked-list of strings.
* List<String> myparam = req.getFormDataParameter("myparam" , LinkedList.class , String.class );
*
* // Parse into a linked-list of linked-lists of strings.
* List<List<String>> myparam = req.getFormDataParameter("myparam" , LinkedList.class , LinkedList.class , String.class );
*
* // Parse into a map of string keys/values.
* Map<String,String> myparam = req.getFormDataParameter("myparam" , TreeMap.class , String.class , String.class );
*
* // Parse into a map containing string keys and values of lists containing beans.
* Map<String,List<MyBean>> myparam = req.getFormDataParameter("myparam" , TreeMap.class , String.class , List.class , MyBean.class );
*
*
* @param name The parameter name.
* @param type
* The type of object to create.
*
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.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
* {@link GenericArrayType}
*
Ignored if the main type is not a map or collection.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
public T get(String name, Type type, Type...args) throws ParseException {
return (T)parse(name, beanSession.getClassMeta(type, args));
}
/**
* Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
* (e.g. "key=1&key=2&key=3" instead of "key=(1,2,3)" )
*
*
* This method must only be called when parsing into classes of type Collection or array.
*
* @param name The parameter name.
* @param type
* The type of object to create.
*
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.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
* {@link GenericArrayType}
*
Ignored if the main type is not a map or collection.
* @return The parameter value converted to the specified class type.
* @throws ParseException
*/
public T getAll(String name, Type type, Type...args) throws ParseException {
return (T)parseAll(name, beanSession.getClassMeta(type, args));
}
/* Workhorse method */
T parse(String name, T def, ClassMeta cm) throws ParseException {
String val = getString(name);
if (val == null)
return def;
return parseValue(val, cm);
}
/* Workhorse method */
T parse(String name, ClassMeta cm) throws ParseException {
String val = getString(name);
if (cm.isPrimitive() && (val == null || val.isEmpty()))
return cm.getPrimitiveDefault();
return parseValue(val, cm);
}
/* Workhorse method */
@SuppressWarnings("rawtypes")
T parseAll(String name, ClassMeta cm) throws ParseException {
String[] p = get(name);
if (p == null)
return null;
if (cm.isArray()) {
List c = new ArrayList();
for (int i = 0; i < p.length; i++)
c.add(parseValue(p[i], cm.getElementType()));
return (T)toArray(c, cm.getElementType().getInnerClass());
} else if (cm.isCollection()) {
try {
Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
for (int i = 0; i < p.length; i++)
c.add(parseValue(p[i], cm.getElementType()));
return (T)c;
} catch (ParseException e) {
throw e;
} catch (Exception e) {
// Typically an instantiation exception.
throw new ParseException(e);
}
}
throw new ParseException("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array.");
}
private T parseValue(String val, ClassMeta c) throws ParseException {
return parser.parse(PartType.FORM_DATA, val, c);
}
/**
* Converts the form data parameters to a readable string.
*
* @param sorted Sort the form data parameters by name.
* @return A JSON string containing the contents of the form data parameters.
*/
public String toString(boolean sorted) {
Map m = (sorted ? new TreeMap() : new LinkedHashMap());
for (Map.Entry e : this.entrySet()) {
String[] v = e.getValue();
m.put(e.getKey(), v.length == 1 ? v[0] : v);
}
return JsonSerializer.DEFAULT_LAX.toString(m);
}
@Override /* Object */
public String toString() {
return toString(false);
}
}