001: package org.apache.turbine.util.parser;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.net.URLDecoder;
023:
024: import java.util.Enumeration;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.Set;
029: import java.util.StringTokenizer;
030:
031: import javax.servlet.http.HttpServletRequest;
032:
033: import org.apache.commons.collections.set.CompositeSet;
034: import org.apache.commons.fileupload.FileItem;
035: import org.apache.commons.lang.ArrayUtils;
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: import org.apache.turbine.TurbineConstants;
040: import org.apache.turbine.services.upload.TurbineUpload;
041: import org.apache.turbine.services.upload.UploadService;
042: import org.apache.turbine.util.TurbineException;
043: import org.apache.turbine.util.pool.Recyclable;
044:
045: /**
046: * DefaultParameterParser is a utility object to handle parsing and
047: * retrieving the data passed via the GET/POST/PATH_INFO arguments.
048: *
049: * <p>NOTE: The name= portion of a name=value pair may be converted
050: * to lowercase or uppercase when the object is initialized and when
051: * new data is added. This behaviour is determined by the url.case.folding
052: * property in TurbineResources.properties. Adding a name/value pair may
053: * overwrite existing name=value pairs if the names match:
054: *
055: * <pre>
056: * ParameterParser pp = data.getParameters();
057: * pp.add("ERROR",1);
058: * pp.add("eRrOr",2);
059: * int result = pp.getInt("ERROR");
060: * </pre>
061: *
062: * In the above example, result is 2.
063: *
064: * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
065: * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
066: * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
067: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
068: * @version $Id: DefaultParameterParser.java 534527 2007-05-02 16:10:59Z tv $
069: */
070: public class DefaultParameterParser extends BaseValueParser implements
071: ParameterParser, Recyclable {
072: /** Logging */
073: private static Log log = LogFactory
074: .getLog(DefaultParameterParser.class);
075:
076: /** The servlet request to parse. */
077: private HttpServletRequest request = null;
078:
079: /** The raw data of a file upload. */
080: private byte[] uploadData = null;
081:
082: /** Map of request parameters to FileItem[]'s */
083: private Map fileParameters = new HashMap();
084:
085: /** Turbine Upload Service reference */
086: private static UploadService uploadService = null;
087:
088: /** Do we have an upload Service? */
089: private static boolean uploadServiceIsAvailable = false;
090:
091: /**
092: * Create a new empty instance of ParameterParser. Uses the
093: * default character encoding (US-ASCII).
094: *
095: * <p>To add name/value pairs to this set of parameters, use the
096: * <code>add()</code> methods.
097: */
098: public DefaultParameterParser() {
099: super ();
100: configureUploadService();
101: }
102:
103: /**
104: * Create a new empty instance of ParameterParser. Takes a
105: * character encoding name to use when converting strings to
106: * bytes.
107: *
108: * <p>To add name/value pairs to this set of parameters, use the
109: * <code>add()</code> methods.
110: *
111: * @param characterEncoding The character encoding of strings.
112: */
113: public DefaultParameterParser(String characterEncoding) {
114: super (characterEncoding);
115: configureUploadService();
116: }
117:
118: /**
119: * Checks for availability of the Upload Service. We do this
120: * check only once at Startup, because the getService() call
121: * is really expensive and we don't have to run it every time
122: * we process a request.
123: */
124: private void configureUploadService() {
125: uploadServiceIsAvailable = TurbineUpload.isAvailable();
126: if (uploadServiceIsAvailable) {
127: uploadService = TurbineUpload.getService();
128: }
129: }
130:
131: /**
132: * Disposes the parser.
133: */
134: public void dispose() {
135: this .request = null;
136: this .uploadData = null;
137: this .fileParameters.clear();
138: super .dispose();
139: }
140:
141: /**
142: * Gets the parsed servlet request.
143: *
144: * @return the parsed servlet request or null.
145: */
146: public HttpServletRequest getRequest() {
147: return request;
148: }
149:
150: /**
151: * Sets the servlet request to the parser. This requires a
152: * valid HttpServletRequest object. It will attempt to parse out
153: * the GET/POST/PATH_INFO data and store the data into a Map.
154: * There are convenience methods for retrieving the data as a
155: * number of different datatypes. The PATH_INFO data must be a
156: * URLEncoded() string.
157: * <p>
158: * To add name/value pairs to this set of parameters, use the
159: * <code>add()</code> methods.
160: *
161: * @param request An HttpServletRequest.
162: */
163: public void setRequest(HttpServletRequest request) {
164: clear();
165:
166: uploadData = null;
167:
168: String enc = request.getCharacterEncoding();
169: setCharacterEncoding(enc != null ? enc
170: : TurbineConstants.PARAMETER_ENCODING_DEFAULT);
171:
172: String contentType = request.getHeader("Content-type");
173:
174: if (uploadServiceIsAvailable && uploadService.getAutomatic()
175: && contentType != null
176: && contentType.startsWith("multipart/form-data")) {
177: if (log.isDebugEnabled()) {
178: log.debug("Running the Turbine Upload Service");
179: }
180:
181: try {
182: TurbineUpload.parseRequest(request, this );
183: } catch (TurbineException e) {
184: log.error("File upload failed", e);
185: }
186: }
187:
188: for (Enumeration names = request.getParameterNames(); names
189: .hasMoreElements();) {
190: String paramName = (String) names.nextElement();
191: add(paramName, request.getParameterValues(paramName));
192: }
193:
194: // Also cache any pathinfo variables that are passed around as
195: // if they are query string data.
196: try {
197: boolean isNameTok = true;
198: String paramName = null;
199: String paramValue = null;
200:
201: for (StringTokenizer st = new StringTokenizer(request
202: .getPathInfo(), "/"); st.hasMoreTokens();) {
203: if (isNameTok) {
204: paramName = URLDecoder.decode(st.nextToken(),
205: getCharacterEncoding());
206: isNameTok = false;
207: } else {
208: paramValue = URLDecoder.decode(st.nextToken(),
209: getCharacterEncoding());
210: if (paramName.length() > 0) {
211: add(paramName, paramValue);
212: }
213: isNameTok = true;
214: }
215: }
216: } catch (Exception e) {
217: // If anything goes wrong above, don't worry about it.
218: // Chances are that the path info was wrong anyways and
219: // things that depend on it being right will fail later
220: // and should be caught later.
221: }
222:
223: this .request = request;
224:
225: if (log.isDebugEnabled()) {
226: log.debug("Parameters found in the Request:");
227: for (Iterator it = keySet().iterator(); it.hasNext();) {
228: String key = (String) it.next();
229: log.debug("Key: " + key + " -> " + getString(key));
230: }
231: }
232: }
233:
234: /**
235: * Sets the uploadData byte[]
236: *
237: * @param uploadData A byte[] with data.
238: */
239: public void setUploadData(byte[] uploadData) {
240: this .uploadData = uploadData;
241: }
242:
243: /**
244: * Gets the uploadData byte[]
245: *
246: * @return uploadData A byte[] with data.
247: */
248: public byte[] getUploadData() {
249: return uploadData;
250: }
251:
252: /**
253: * Add a FileItem object as a parameters. If there are any
254: * FileItems already associated with the name, append to the
255: * array. The reason for this is that RFC 1867 allows multiple
256: * files to be associated with single HTML input element.
257: *
258: * @param name A String with the name.
259: * @param value A FileItem with the value.
260: * @deprecated Use add(String name, FileItem item)
261: */
262: public void append(String name, FileItem item) {
263: add(name, item);
264: }
265:
266: /**
267: * Add a FileItem object as a parameters. If there are any
268: * FileItems already associated with the name, append to the
269: * array. The reason for this is that RFC 1867 allows multiple
270: * files to be associated with single HTML input element.
271: *
272: * @param name A String with the name.
273: * @param value A FileItem with the value.
274: */
275: public void add(String name, FileItem item) {
276: FileItem[] items = getFileItemParam(name);
277: items = (FileItem[]) ArrayUtils.add(items, item);
278: putFileItemParam(name, items);
279: }
280:
281: /**
282: * Gets the set of keys (FileItems and regular parameters)
283: *
284: * @return A <code>Set</code> of the keys.
285: */
286: public Set keySet() {
287: return new CompositeSet(new Set[] { super .keySet(),
288: fileParameters.keySet() });
289: }
290:
291: /**
292: * Determine whether a given key has been inserted. All keys are
293: * stored in lowercase strings, so override method to account for
294: * this.
295: *
296: * @param key An Object with the key to search for.
297: * @return True if the object is found.
298: */
299: public boolean containsKey(Object key) {
300: if (super .containsKey(key)) {
301: return true;
302: }
303:
304: return fileParameters.containsKey(convert(String.valueOf(key)));
305: }
306:
307: /**
308: * Return a FileItem object for the given name. If the name does
309: * not exist or the object stored is not a FileItem, return null.
310: *
311: * @param name A String with the name.
312: * @return A FileItem.
313: */
314: public FileItem getFileItem(String name) {
315: FileItem[] value = getFileItemParam(name);
316:
317: return (value == null || value.length == 0) ? null : value[0];
318: }
319:
320: /**
321: * Return an array of FileItem objects for the given name. If the
322: * name does not exist, return null.
323: *
324: * @param name A String with the name.
325: * @return An Array of FileItems or null.
326: */
327: public FileItem[] getFileItems(String name) {
328: return getFileItemParam(name);
329: }
330:
331: /**
332: * Puts a key into the parameters map. Makes sure that the name is always
333: * mapped correctly. This method also enforces the usage of arrays for the
334: * parameters.
335: *
336: * @param name A String with the name.
337: * @param value An array of Objects with the values.
338: *
339: */
340: protected void putFileItemParam(final String name,
341: final FileItem[] value) {
342: String key = convert(name);
343: if (key != null) {
344: fileParameters.put(key, value);
345: }
346: }
347:
348: /**
349: * fetches a key from the parameters map. Makes sure that the name is
350: * always mapped correctly.
351: *
352: * @param name A string with the name
353: *
354: * @return the value object array or null if not set
355: */
356: protected FileItem[] getFileItemParam(final String name) {
357: String key = convert(name);
358:
359: return (key != null) ? (FileItem[]) fileParameters.get(key)
360: : null;
361: }
362:
363: /**
364: * This method is only used in toString() and can be used by
365: * derived classes to add their local parameters to the toString()
366:
367: * @param name A string with the name
368: *
369: * @return the value object array or null if not set
370: */
371: protected Object[] getToStringParam(final String name) {
372: if (super.containsKey(name)) {
373: return getParam(name);
374: } else {
375: return getFileItemParam(name);
376: }
377: }
378: }
|