001: /**
002: * URLDecoder.java
003: *
004: *
005: * Copyright (c) 2003 Rimfaxe ApS (www.rimfaxe.com).
006: * All rights reserved.
007: *
008: * This package is written by Lars Andersen <lars@rimfaxe.com>
009: * and licensed by Rimfaxe ApS.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions
013: * are met:
014: *
015: * 1. Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * 2. Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in
020: * the documentation and/or other materials provided with the
021: * distribution.
022: *
023: * 3. The end-user documentation included with the redistribution, if
024: * any, must include the following acknowlegement:
025: * "This product includes software developed by Rimfaxe ApS
026: (www.rimfaxe.com)"
027: * Alternately, this acknowlegement may appear in the software itself,
028: * if and wherever such third-party acknowlegements normally appear.
029: *
030: * 4. The names "Rimfaxe", "Rimfaxe Software", "Lars Andersen" and
031: * "Rimfaxe WebServer" must not be used to endorse or promote products
032: * derived from this software without prior written permission. For written
033: * permission, please contact info@rimfaxe.com
034: *
035: * 5. Products derived from this software may not be called "Rimfaxe"
036: * nor may "Rimfaxe" appear in their names without prior written
037: * permission of the Rimfaxe ApS.
038: *
039: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
040: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
041: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
042: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
043: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
044: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
045: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
046: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
047: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
048: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
049: * SUCH DAMAGE.
050: *
051: */package com.rimfaxe.webserver.forms;
052:
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.io.InputStreamReader;
056: import java.io.Reader;
057:
058: import java.util.Hashtable;
059: import java.util.Enumeration;
060:
061: /**
062: * Form data decoder.
063: * This class takes an InputStream and decodes it in compliance to the
064: * <b>application/x-www-form-urlencoded</b> MIME type.
065: */
066:
067: public class URLDecoder {
068: public final static String EMPTY_VALUE = "";
069:
070: int ch = -1;
071: Hashtable values = null;
072: byte buffer[] = new byte[1024];
073: int bsize = 0;
074: Reader in = null;
075: boolean overide = true;
076:
077: private void append(int c) {
078: if (bsize + 1 >= buffer.length) {
079: byte nb[] = new byte[buffer.length * 2];
080: System.arraycopy(buffer, 0, nb, 0, buffer.length);
081: buffer = nb;
082: }
083: buffer[bsize++] = (byte) c;
084: }
085:
086: /**
087: * Get an enumeration of the variable names.
088: * @return An enumeration continaing one element per key.
089: */
090: public Enumeration keys() {
091: return values.keys();
092: }
093:
094: /**
095: * Define a new variable in this data set.
096: * @param name The name of the variable.
097: * @param value Its value.
098: */
099: protected void addVariable(String var, String val) {
100: if (overide) {
101: values.put(var, val);
102: } else {
103: Object value = values.get(var);
104: if (value == null) {
105: values.put(var, val);
106: } else if (value instanceof String[]) {
107: String olds[] = (String[]) value;
108: String vals[] = new String[olds.length + 1];
109: System.arraycopy(olds, 0, vals, 0, olds.length);
110: vals[olds.length] = val;
111: values.put(var, vals);
112: } else if (value instanceof String) {
113: String vals[] = new String[2];
114: vals[0] = (String) value;
115: vals[1] = val;
116: values.put(var, vals);
117: }
118: }
119: }
120:
121: /**
122: * Get the values of the variable, as an array.
123: * Use this method when you have turned off the <em>overide</em> flag
124: * in the constructor of this object. This will always return either an
125: * array of Strings or <strong>null</strong>.
126: * <p>I use this in the PICS label bureau, and I pretty sure this is not a
127: * good reason to have it here.
128: * @param name The name of the variable to look for.
129: * @return An String[] having one entry per variable's value, or <strong>
130: * null</strong> if none was found.
131: */
132: public String[] getMultipleValues(String name) {
133: if (overide)
134: throw new RuntimeException(this .getClass().getName()
135: + "[getMultipleValues]: " + " overide not set !");
136: Object value = values.get(name);
137: if (value instanceof String[]) {
138: return (String[]) value;
139: } else {
140: String vals[] = new String[1];
141: vals[0] = (String) value;
142: values.put(name, vals);
143: return vals;
144: }
145: }
146:
147: /**
148: * Get the value of a variable.
149: * If you have allowed the decoder to accumulate multiple values for the
150: * same variable, this method casts of the value to a String may fail
151: * <em>at runtime</em>.
152: * @param name The name of the variable whose value is to be fetched.
153: * @return Its values, which is always provided as a String, or null.
154: */
155: public String getValue(String name) {
156: Object value = values.get(name);
157: if ((value != null) && !(value instanceof String))
158: throw new RuntimeException(this .getClass().getName()
159: + "[getValue]:" + " use getMultipleValues in:\n\t"
160: + name + " " + value);
161: return (String) value;
162: }
163:
164: /**
165: * Parse our input stream following the
166: * <b>application/x-www-form-urlencoded</b> specification.
167: * @return The raw bindings obtained from parsing the stream, as a
168: * Hashtable instance.
169: * @exception IOException When IO error occurs while reading the stream.
170: * @exception URLDecoderException If the format is invalid.
171: */
172:
173: public Hashtable parse() throws IOException, URLDecoderException {
174: String key = null;
175:
176: read_loop: ch = in.read();
177: while (true) {
178: switch (ch) {
179: case '+':
180: append(' ');
181: break;
182: case '%':
183: int hi,
184: lo;
185: if ((hi = ch = in.read()) == -1)
186: throw new URLDecoderException("Invalid escape seq.");
187: if ((lo = ch = in.read()) == -1)
188: throw new URLDecoderException("Invalid escape seq.");
189: hi = (Character.isDigit((char) hi) ? (hi - '0')
190: : 10 + (Character.toUpperCase((char) hi) - 'A'));
191: lo = (Character.isDigit((char) lo) ? (lo - '0')
192: : 10 + (Character.toUpperCase((char) lo) - 'A'));
193: append((char) (((byte) lo) | (((byte) hi) << 4)));
194: break;
195: case '&':
196: if (key == null) {
197: // We only get a simple key (with no value)
198: addVariable(new String(buffer, 0, bsize),
199: EMPTY_VALUE);
200: bsize = 0;
201: } else {
202: addVariable(key, new String(buffer, 0, bsize));
203: key = null;
204: bsize = 0;
205: }
206: break;
207: case ';': // HTML4.0: appendix b2.2: use of ";" in place of "&"
208: if (key == null) {
209: // We only get a simple key (with no value)
210: addVariable(new String(buffer, 0, bsize),
211: EMPTY_VALUE);
212: bsize = 0;
213: } else {
214: addVariable(key, new String(buffer, 0, bsize));
215: key = null;
216: bsize = 0;
217: }
218: break;
219: case '=':
220: if (key != null) {
221: append(ch);
222: } else {
223: key = new String(buffer, 0, bsize);
224: bsize = 0;
225: }
226: break;
227: case -1:
228: // Same as '&', except that we return
229: if (key == null) {
230: // We only get a simple key (with no value)
231: addVariable(new String(buffer, 0, bsize),
232: EMPTY_VALUE);
233: bsize = 0;
234: } else {
235: addVariable(key, new String(buffer, 0, bsize));
236: key = null;
237: bsize = 0;
238: }
239: return values;
240: default:
241: append(ch);
242: break;
243: }
244: ch = in.read();
245: }
246: }
247:
248: /**
249: * Create an URLDecoder for the given stream.
250: * @param in The input stream to be parsed.
251: * @param list Tells how to handle multiple settings of the same variable.
252: * When <strong>false</strong>, mutiple settings to the same variable
253: * will accumulate the value into an array, returned by getValue().
254: * Otherwise, the last assignment will overide any previous assignment.
255: */
256: public URLDecoder(Reader in, boolean overide) {
257: this .values = new Hashtable(23);
258: this .in = in;
259: this .ch = -1;
260: this .overide = overide;
261: }
262:
263: /**
264: * Create an URLDecoder for the given stream.
265: * @param in The input stream to be parsed.
266: * @param list Tells how to handle multiple settings of the same variable.
267: * When <strong>false</strong>, mutiple settings to the same variable
268: * will accumulate the value into an array, returned by getValue().
269: * Otherwise, the last assignment will overide any previous assignment.
270: */
271: public URLDecoder(Reader in) {
272: this (in, true);
273: }
274:
275: /**
276: * Create an URLDecoder for the given stream.
277: * @param in The input stream to be parsed.
278: * @param list Tells how to handle multiple settings of the same variable.
279: * When <strong>false</strong>, mutiple settings to the same variable
280: * will accumulate the value into an array, returned by getValue().
281: * Otherwise, the last assignment will overide any previous assignment.
282: */
283:
284: public URLDecoder(InputStream in, boolean overide) {
285: this .values = new Hashtable(23);
286: this .in = new InputStreamReader(in);
287: this .ch = -1;
288: this .overide = overide;
289: }
290:
291: /**
292: * Create an URLDecoder for the given stream.
293: * Default constructor, which will keep track only of the last setting
294: * of the same variable (if ever it gets assigned multiply).
295: * @param in The input stream to be parsed.
296: */
297: public URLDecoder(InputStream in) {
298: this (in, true);
299: }
300:
301: }
|