1 package org.apache.turbine.util.parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.net.URLDecoder;
20
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.StringTokenizer;
26
27 import javax.servlet.http.HttpServletRequest;
28
29 import org.apache.commons.fileupload.FileItem;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import org.apache.turbine.services.upload.TurbineUpload;
35 import org.apache.turbine.services.upload.UploadService;
36
37 import org.apache.turbine.util.TurbineException;
38 import org.apache.turbine.util.pool.Recyclable;
39
40 /***
41 * DefaultParameterParser is a utility object to handle parsing and
42 * retrieving the data passed via the GET/POST/PATH_INFO arguments.
43 *
44 * <p>NOTE: The name= portion of a name=value pair may be converted
45 * to lowercase or uppercase when the object is initialized and when
46 * new data is added. This behaviour is determined by the url.case.folding
47 * property in TurbineResources.properties. Adding a name/value pair may
48 * overwrite existing name=value pairs if the names match:
49 *
50 * <pre>
51 * ParameterParser pp = data.getParameters();
52 * pp.add("ERROR",1);
53 * pp.add("eRrOr",2);
54 * int result = pp.getInt("ERROR");
55 * </pre>
56 *
57 * In the above example, result is 2.
58 *
59 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
60 * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
61 * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
62 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
63 * @version $Id: DefaultParameterParser.java,v 1.20.2.2 2004/05/20 03:33:43 seade Exp $
64 */
65 public class DefaultParameterParser
66 extends BaseValueParser
67 implements ParameterParser, Recyclable
68 {
69 /*** Logging */
70 private static Log log = LogFactory.getLog(DefaultParameterParser.class);
71
72 /*** The servlet request to parse. */
73 private HttpServletRequest request = null;
74
75 /*** The raw data of a file upload. */
76 private byte[] uploadData = null;
77
78 /*** Map of request parameters to FileItem[]'s */
79 private Map fileParameters = new HashMap();
80
81 /*** Turbine Upload Service reference */
82 private static UploadService uploadService = null;
83
84 /*** Do we have an upload Service? */
85 private static boolean uploadServiceIsAvailable = false;
86
87 /***
88 * Create a new empty instance of ParameterParser. Uses the
89 * default character encoding (US-ASCII).
90 *
91 * <p>To add name/value pairs to this set of parameters, use the
92 * <code>add()</code> methods.
93 */
94 public DefaultParameterParser()
95 {
96 super();
97 configureUploadService();
98 }
99
100 /***
101 * Create a new empty instance of ParameterParser. Takes a
102 * character encoding name to use when converting strings to
103 * bytes.
104 *
105 * <p>To add name/value pairs to this set of parameters, use the
106 * <code>add()</code> methods.
107 *
108 * @param characterEncoding The character encoding of strings.
109 */
110 public DefaultParameterParser(String characterEncoding)
111 {
112 super (characterEncoding);
113 configureUploadService();
114 }
115
116 /***
117 * Checks for availability of the Upload Service. We do this
118 * check only once at Startup, because the getService() call
119 * is really expensive and we don't have to run it every time
120 * we process a request.
121 */
122 private void configureUploadService()
123 {
124 uploadServiceIsAvailable = TurbineUpload.isAvailable();
125 if (uploadServiceIsAvailable)
126 {
127 uploadService = TurbineUpload.getService();
128 }
129 }
130
131 /***
132 * Disposes the parser.
133 */
134 public void dispose()
135 {
136 this.request = null;
137 this.uploadData = null;
138 this.fileParameters.clear();
139 super.dispose();
140 }
141
142 /***
143 * Gets the parsed servlet request.
144 *
145 * @return the parsed servlet request or null.
146 */
147 public HttpServletRequest getRequest()
148 {
149 return this.request;
150 }
151
152 /***
153 * Sets the servlet request to be parser. This requires a
154 * valid HttpServletRequest object. It will attempt to parse out
155 * the GET/POST/PATH_INFO data and store the data into a Map.
156 * There are convenience methods for retrieving the data as a
157 * number of different datatypes. The PATH_INFO data must be a
158 * URLEncoded() string.
159 * <p>
160 * To add name/value pairs to this set of parameters, use the
161 * <code>add()</code> methods.
162 *
163 * @param request An HttpServletRequest.
164 */
165 public void setRequest(HttpServletRequest request)
166 {
167 clear();
168
169 uploadData = null;
170
171 String enc = request.getCharacterEncoding();
172 setCharacterEncoding(enc != null ? enc : "US-ASCII");
173
174
175 String tmp = null;
176
177 tmp = request.getHeader("Content-type");
178
179 if (uploadServiceIsAvailable
180 && uploadService.getAutomatic()
181 && tmp != null
182 && tmp.startsWith("multipart/form-data"))
183 {
184 log.debug("Running the Turbine Upload Service");
185 try
186 {
187 TurbineUpload.parseRequest(request, this);
188 }
189 catch (TurbineException e)
190 {
191 log.error("File upload failed", e);
192 }
193 }
194
195 for (Enumeration names = request.getParameterNames();
196 names.hasMoreElements();)
197 {
198 tmp = (String) names.nextElement();
199 add(convert(tmp),
200 request.getParameterValues(tmp));
201 }
202
203
204
205 try
206 {
207 StringTokenizer st =
208 new StringTokenizer(request.getPathInfo(), "/");
209 boolean isNameTok = true;
210 String pathPart = null;
211 while (st.hasMoreTokens())
212 {
213 if (isNameTok)
214 {
215 tmp = URLDecoder.decode(st.nextToken());
216 isNameTok = false;
217 }
218 else
219 {
220 pathPart = URLDecoder.decode(st.nextToken());
221 if (tmp.length() > 0)
222 {
223 add(convert(tmp), pathPart);
224 }
225 isNameTok = true;
226 }
227 }
228 }
229 catch (Exception e)
230 {
231
232
233
234
235 }
236
237 this.request = request;
238
239 if (log.isDebugEnabled())
240 {
241 log.debug("Parameters found in the Request:");
242 for (Iterator it = keySet().iterator(); it.hasNext();)
243 {
244 String key = (String) it.next();
245 log.debug("Key: " + key + " -> " + getString(key));
246 }
247 }
248 }
249
250 /***
251 * Sets the uploadData byte[]
252 *
253 * @param uploadData A byte[] with data.
254 */
255 public void setUploadData(byte[] uploadData)
256 {
257 this.uploadData = uploadData;
258 }
259
260 /***
261 * Gets the uploadData byte[]
262 *
263 * @return uploadData A byte[] with data.
264 */
265 public byte[] getUploadData()
266 {
267 return this.uploadData;
268 }
269
270 /***
271 * Add a FileItem object as a parameters. If there are any
272 * FileItems already associated with the name, append to the
273 * array. The reason for this is that RFC 1867 allows multiple
274 * files to be associated with single HTML input element.
275 *
276 * @param name A String with the name.
277 * @param value A FileItem with the value.
278 */
279 public void append(String name, FileItem value)
280 {
281 FileItem[] items = this.getFileItems(name);
282 if (items == null)
283 {
284 items = new FileItem[1];
285 items[0] = value;
286 fileParameters.put(convert(name), items);
287 }
288 else
289 {
290 FileItem[] newItems = new FileItem[items.length + 1];
291 System.arraycopy(items, 0, newItems, 0, items.length);
292 newItems[items.length] = value;
293 fileParameters.put(convert(name), newItems);
294 }
295 }
296
297 /***
298 * Return a FileItem object for the given name. If the name does
299 * not exist or the object stored is not a FileItem, return null.
300 *
301 * @param name A String with the name.
302 * @return A FileItem.
303 */
304 public FileItem getFileItem(String name)
305 {
306 try
307 {
308 FileItem value = null;
309 Object object = fileParameters.get(convert(name));
310 if (object != null)
311 {
312 value = ((FileItem[]) object)[0];
313 }
314 return value;
315 }
316 catch (ClassCastException e)
317 {
318 log.error("Parameter ("
319 + name + ") is not an instance of FileItem", e);
320 return null;
321 }
322 }
323
324 /***
325 * Return an array of FileItem objects for the given name. If the
326 * name does not exist or the object stored is not a FileItem
327 * array, return null.
328 *
329 * @param name A String with the name.
330 * @return A FileItem[].
331 */
332 public FileItem[] getFileItems(String name)
333 {
334 try
335 {
336 return (FileItem[]) fileParameters.get(convert(name));
337 }
338 catch (ClassCastException e)
339 {
340 log.error("Parameter ("
341 + name + ") is not an instance of FileItem[]", e);
342 return null;
343 }
344 }
345 }