blob: f74370ea5228040946a8298b5cc6816441c309b2 [file] [log] [blame]
/*
* Copyright 2008 Malcolm A. Edgar
*
* Licensed 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 net.sf.click;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import net.sf.click.util.ClickUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
/**
* Provides a custom HttpServletRequest class for shielding users from
* multipart request parameters. Thus calling request.getParameter(String)
* will still work properly.
*
* @author Bob Schellink
* @author Malcolm Edgar
*/
class ClickRequestWrapper extends HttpServletRequestWrapper {
/**
* The <tt>FileItem</tt> objects for <tt>"multipart"</tt> POST requests.
*/
private final Map fileItemMap;
/** The request is a multi-part file upload POST request. */
private final boolean isMultipartRequest;
/** The wrapped servlet request. */
private final HttpServletRequest request;
/** The map of request parameter values. */
private final Map requestParameterMap;
// ----------------------------------------------------------- Constructors
/**
* @see HttpServletRequestWrapper(HttpServletRequest)
*/
ClickRequestWrapper(HttpServletRequest request, FileItemFactory fileItemFactory) {
super(request);
this.isMultipartRequest = ClickUtils.isMultipartRequest(request);
this.request = request;
if (isMultipartRequest) {
// If this request is multipart, populate two maps, one for normal
// request parameters, the other for all uploaded files
FileUploadBase fileUpload = new ServletFileUpload(fileItemFactory);
Map requestParams = new HashMap();
Map fileItems = new HashMap();
try {
ServletRequestContext srvContext =
new ServletRequestContext(request);
List itemsList = fileUpload.parseRequest(srvContext);
for (int i = 0; i < itemsList.size(); i++) {
FileItem fileItem = (FileItem) itemsList.get(i);
String name = fileItem.getFieldName();
String value = null;
//Form fields are placed in the request parameter map,
//while file uploads are placed in the file item map.
if (fileItem.isFormField()) {
if (request.getCharacterEncoding() == null) {
value = fileItem.getString();
} else {
try {
value = fileItem.getString(request.getCharacterEncoding());
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
}
//Add the form field value to the parameters
addToMapAsString(requestParams, name, value);
} else {
//Add the file item to the list of file items
addToMapAsFileItem(fileItems, name, fileItem);
}
}
fileItemMap = Collections.unmodifiableMap(fileItems);
requestParameterMap = Collections.unmodifiableMap(requestParams);
} catch (FileUploadException fue) {
throw new RuntimeException(fue);
}
} else {
fileItemMap = Collections.EMPTY_MAP;
requestParameterMap = Collections.EMPTY_MAP;
}
}
// --------------------------------------------------------- Public Methods
/**
* Returns a map of <tt>FileItem arrays</tt> keyed on request parameter
* name for "multipart" POST requests (file uploads). Thus each map entry
* will consist of one or more <tt>FileItem</tt> objects.
*
* @return map of <tt>FileItem arrays</tt> keyed on request parameter name
* for "multipart" POST requests
*/
public Map getFileItemMap() {
return fileItemMap;
}
/**
* @see javax.servlet.ServletRequest#getParameter(String)
*/
public String getParameter(String name) {
if (isMultipartRequest) {
Object value = requestParameterMap.get(name);
if (value instanceof String) {
return (String) value;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length >= 1) {
return array[0];
} else {
return null;
}
}
return (value == null ? null : value.toString());
} else {
return request.getParameter(name);
}
}
/**
* @see javax.servlet.ServletRequest#getParameterNames()
*/
public Enumeration getParameterNames() {
if (isMultipartRequest) {
return Collections.enumeration(requestParameterMap.keySet());
} else {
return request.getParameterNames();
}
}
/**
* @see javax.servlet.ServletRequest#getParameterValues(String)
*/
public String[] getParameterValues(String name) {
if (isMultipartRequest) {
Object values = requestParameterMap.get(name);
if (values instanceof String) {
return new String[] { values.toString() };
}
if (values instanceof String[]) {
return (String[]) values;
} else {
return null;
}
} else {
return request.getParameterValues(name);
}
}
/**
* @see javax.servlet.ServletRequest#getParameterMap()
*/
public Map getParameterMap() {
if (isMultipartRequest) {
return requestParameterMap;
} else {
return request.getParameterMap();
}
}
// -------------------------------------------------------- Private methods
/**
* Stores the specified value in a FileItem array in the map, under the
* specified name. Thus two values stored under the same name will be
* stored in the same array.
*
* @param map the map to add the specified name and value to
* @param name the name of the map key
* @param value the value to add to the FileItem array
*/
private void addToMapAsFileItem(Map map, String name, FileItem value) {
FileItem[] oldValues = (FileItem[]) map.get(name);
FileItem[] newValues = null;
if (oldValues == null) {
newValues = new FileItem[] {value};
} else {
newValues = new FileItem[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}
/**
* Stores the specified value in an String array in the map, under the
* specified name. Thus two values stored under the same name will be
* stored in the same array.
*
* @param map the map to add the specified name and value to
* @param name the name of the map key
* @param value the value to add to the string array
*/
private void addToMapAsString(Map map, String name, String value) {
String[] oldValues = (String[]) map.get(name);
String[] newValues = null;
if (oldValues == null) {
newValues = new String[] {value};
} else {
newValues = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}
}