001: /*
002: * Parse CGI query data.
003: * Copyright (C) 2001-2004 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * See COPYING.TXT for details.
017: */
018:
019: package com.Ostermiller.util;
020:
021: import java.io.*;
022: import java.net.URLDecoder;
023: import java.util.*;
024:
025: /**
026: * Parses query string data from a CGI request into name value pairs.
027: * <p>
028: * More information about this class is available from <a target="_top" href=
029: * "http://ostermiller.org/utils/CGIParser.html">ostermiller.org</a>.
030: *
031: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
032: * @since ostermillerutils 1.00.00
033: */
034: public class CGIParser {
035:
036: /**
037: * Hash of names to Array Lists of values.
038: */
039: private HashMap<String, ArrayList<String>> nameValuePairHash = new HashMap<String, ArrayList<String>>();
040:
041: /**
042: * Array list of NameValuePair objects.
043: */
044: private LinkedList<NameValuePair> nameValuePairList = new LinkedList<NameValuePair>();
045:
046: /**
047: * Extract the name, value pairs from the given input stream and
048: * make them available for retrieval.
049: * <p>
050: * The stream is read until the stream contains no more bytes.
051: * <p>
052: * Byte to character conversion on the stream is done according the platforms
053: * default character encoding.
054: *
055: * @param in Stream containing CGI Encoded name value pairs.
056: * @throws IOException If an input error occurs
057: * @deprecated This method does not decode URLEncoded values properly. Please use a method that specifies a character set.
058: *
059: * @since ostermillerutils 1.00.00
060: */
061: @Deprecated
062: public CGIParser(InputStream in) throws IOException {
063: if (in == null)
064: return;
065: CGILexer lex = new CGILexer(in);
066: parse(lex, "ISO-8859-1");
067: }
068:
069: /**
070: * Extract the name, value pairs from the given input stream and
071: * make them available for retrieval.
072: * <p>
073: * The stream is read until the stream contains no more bytes.
074: * <p>
075: * The character set is used both when converting the byte stream to
076: * a character stream and when decoding URL decoded parameters.
077: *
078: * @param in Stream containing CGI Encoded name value pairs.
079: * @param charset Character encoding to use when converting bytes to characters
080: * @throws IOException If an input error occurs
081: * @throws UnsupportedEncodingException If the character set provided is not recognized
082: *
083: * @since ostermillerutils 1.03.00
084: */
085: public CGIParser(InputStream in, String charset)
086: throws IOException, UnsupportedEncodingException {
087: if (in == null)
088: return;
089: CGILexer lex = new CGILexer(new InputStreamReader(in, charset));
090: parse(lex, charset);
091: }
092:
093: /**
094: * Extract the name, value pairs from the given reader and
095: * make them available for retrieval.
096: * <p>
097: * The reader is read until the stream contains no more characters.
098: *
099: * @param in Reader containing CGI Encoded name value pairs.
100: * @throws IOException If an input error occurs
101: * @deprecated This method does not decode URLEncoded values properly. Please use a method that specifies a character set.
102: *
103: * @since ostermillerutils 1.00.00
104: */
105: @Deprecated
106: public CGIParser(Reader in) throws IOException {
107: if (in == null)
108: return;
109: CGILexer lex = new CGILexer(in);
110: parse(lex, "ISO-8859-1");
111: }
112:
113: /**
114: * Extract the name, value pairs from the given reader and
115: * make them available for retrieval.
116: * <p>
117: * The reader is read until the stream contains no more characters.
118: *
119: * @param in Reader containing CGI Encoded name value pairs.
120: * @param charset Character encoding to use when converting bytes to characters
121: * @throws IOException If an input error occurs
122: * @throws UnsupportedEncodingException If the character set provided is not recognized
123: *
124: * @since ostermillerutils 1.03.00
125: */
126: public CGIParser(Reader in, String charset) throws IOException,
127: UnsupportedEncodingException {
128: if (in == null)
129: return;
130: CGILexer lex = new CGILexer(in);
131: parse(lex, charset);
132: }
133:
134: /**
135: * Extract the name, value pairs from the given string and
136: * make them available for retrieval.
137: *
138: * @param s CGI Encoded name value pairs.
139: * @deprecated This method does not decode URLEncoded values properly. Please use a method that specifies a character set.
140: *
141: * @since ostermillerutils 1.00.00
142: */
143: @Deprecated
144: public CGIParser(String s) {
145: if (s == null)
146: return;
147: try {
148: CGILexer lex = new CGILexer(new StringReader(s));
149: parse(lex, "ISO-8859-1");
150: } catch (IOException x) {
151: // This shouldn't be able to happen from a string.
152: throw new RuntimeException(x);
153: }
154: }
155:
156: /**
157: * Extract the name, value pairs from the given string and
158: * make them available for retrieval.
159: *
160: * @param s CGI Encoded name value pairs.
161: * @param charset Character encoding to use when converting bytes to characters
162: * @throws UnsupportedEncodingException If the character set provided is not recognized
163: *
164: * @since ostermillerutils 1.03.00
165: */
166: public CGIParser(String s, String charset)
167: throws UnsupportedEncodingException {
168: if (s == null)
169: return;
170: try {
171: CGILexer lex = new CGILexer(new StringReader(s));
172: parse(lex, charset);
173: } catch (UnsupportedEncodingException uex) {
174: throw uex;
175: } catch (IOException x) {
176: // This shouldn't be able to happen from a string.
177: throw new RuntimeException(x);
178: }
179: }
180:
181: /**
182: * Retrieve the name value pairs from the lexer and hash
183: * them in the CGI hash table.
184: *
185: * @param lex Lexer that will return the tokens.
186: * @throws IOException If an input error occurs
187: *
188: * @since ostermillerutils 1.00.00
189: */
190: private void parse(CGILexer lex, String charset)
191: throws IOException, UnsupportedEncodingException {
192: String nameValue, name, value;
193: while ((nameValue = lex.nextToken()) != null) {
194: int equalInd = nameValue.indexOf("=");
195: if (equalInd == -1) {
196: name = nameValue;
197: value = "";
198: } else {
199: name = nameValue.substring(0, equalInd);
200: value = nameValue.substring(equalInd + 1, nameValue
201: .length());
202: }
203: try {
204: name = URLDecoder.decode(name, charset);
205: } catch (IllegalArgumentException iax) {
206: // May be thrown for for illegal escape sequences such as %s
207: name = "";
208: }
209: try {
210: value = URLDecoder.decode(value, charset);
211: } catch (IllegalArgumentException iax) {
212: // May be thrown for for illegal escape sequences such as %s
213: value = "";
214: }
215:
216: // Hash
217: ArrayList<String> values = nameValuePairHash.get(name);
218: if (values == null) {
219: values = new ArrayList<String>();
220: }
221: values.add(value);
222: nameValuePairHash.put(name, values);
223:
224: // List
225: nameValuePairList.add(new NameValuePair(name, value));
226: }
227: }
228:
229: /**
230: * Returns an array of String objects containing all of the values the given request
231: * parameter has, or null if the parameter does not exist.
232: * <p>
233: * If the parameter has a single value, the array has a length of 1.
234: * @param name a String containing the name of the parameter whose value is requested
235: * @return an array of String objects containing the parameter's values
236: *
237: * @since ostermillerutils 1.00.00
238: */
239: public String[] getParameterValues(String name) {
240: ArrayList<String> values = nameValuePairHash.get(name);
241: if (values == null) {
242: return null;
243: }
244: String[] valArray = new String[values.size()];
245: return (values.toArray(valArray));
246: }
247:
248: /**
249: * Set a name value pair as used in a URL.
250: * This method will replace any previously defined values with the single
251: * value specified. If the value is null, the name is removed.
252: *
253: * @param name a String specifying the name of the parameter.
254: * @param value a String specifying the value of the single parameter, or null to remove.
255: *
256: * @since ostermillerutils 1.02.15
257: */
258: public void setParameter(String name, String value) {
259: Iterator<NameValuePair> listIterator = nameValuePairList
260: .iterator();
261: while (listIterator.hasNext()) {
262: NameValuePair nameValuePair = listIterator.next();
263: if (nameValuePair.getName().equals(name)) {
264: listIterator.remove();
265: }
266: }
267:
268: if (value == null) {
269: nameValuePairHash.remove(name);
270: return;
271: }
272: ArrayList<String> values = nameValuePairHash.get(name);
273: if (values == null) {
274: values = new ArrayList<String>();
275: }
276: values.clear();
277: values.add(value);
278: nameValuePairHash.put(name, values);
279: nameValuePairList.add(new NameValuePair(name, value));
280: }
281:
282: /**
283: * Set a name value pair as used in a URL.
284: * This method will replace any previously defined values with the single
285: * value specified. If values is null or empty, the name is removed.
286: *
287: * @param name a String specifying the name of the parameter.
288: * @param values a String array specifying the values for the parameter, or null to remove.
289: * @throws NullPointerException if any of the values is null.
290: *
291: * @since ostermillerutils 1.02.15
292: */
293: public void setParameter(String name, String[] values) {
294: Iterator<NameValuePair> listIterator = nameValuePairList
295: .iterator();
296: while (listIterator.hasNext()) {
297: NameValuePair nameValuePair = listIterator.next();
298: if (nameValuePair.getName().equals(name)) {
299: listIterator.remove();
300: }
301: }
302:
303: if (values == null || values.length == 0) {
304: nameValuePairHash.remove(name);
305: return;
306: }
307: for (String element : values) {
308: if (element == null)
309: throw new NullPointerException();
310: }
311: ArrayList<String> valuesVec = nameValuePairHash.get(name);
312: if (valuesVec == null) {
313: valuesVec = new ArrayList<String>();
314: }
315: valuesVec.clear();
316: for (String element : values) {
317: valuesVec.add(element);
318: nameValuePairList.add(new NameValuePair(name, element));
319: }
320: nameValuePairHash.put(name, valuesVec);
321: }
322:
323: /**
324: * Set a name value pair as used in a URL.
325: * This method will add to any previously defined values the values
326: * specified. If value is null, this method has no effect.
327: *
328: * @param name a String specifying the name of the parameter.
329: * @param value a String specifying the value of the single parameter, or null to remove.
330: *
331: * @since ostermillerutils 1.02.15
332: */
333: public void addParameter(String name, String value) {
334: if (value == null) {
335: return;
336: }
337: ArrayList<String> values = nameValuePairHash.get(name);
338: if (values == null) {
339: values = new ArrayList<String>();
340: }
341: values.add(value);
342: nameValuePairHash.put(name, values);
343: nameValuePairList.add(new NameValuePair(name, value));
344: }
345:
346: /**
347: * Set a name value pair as used in a URL.
348: * This method will add to any previously defined values the values
349: * specified. If values is null, this method has no effect.
350: *
351: * @param name a String specifying the name of the parameter.
352: * @param values a String array specifying the values of the parameter, or null to remove.
353: * @throws NullPointerException if any of the values is null.
354: *
355: * @since ostermillerutils 1.02.15
356: */
357: public void addParameter(String name, String[] values) {
358: if (values == null || values.length == 0) {
359: return;
360: }
361: for (String element : values) {
362: if (element == null)
363: throw new NullPointerException();
364: }
365: ArrayList<String> valuesVec = nameValuePairHash.get(name);
366: if (valuesVec == null) {
367: valuesVec = new ArrayList<String>();
368: }
369: for (String element : values) {
370: valuesVec.add(element);
371: nameValuePairList.add(new NameValuePair(name, element));
372: }
373: nameValuePairHash.put(name, valuesVec);
374: }
375:
376: /**
377: * Returns the value of a request parameter as a String, or null if the parameter does
378: * not exist. Request parameters are extra information sent with the request.
379: * <p>
380: * You should only use this method when you are sure the parameter has only one value.
381: * If the parameter might have more than one value, use getParameterValues(java.lang.String).
382: * <P>
383: * If you use this method with a multiple valued parameter, the value returned is equal to
384: * the first value in the array returned by getParameterValues.
385: *
386: * @param name a String specifying the name of the parameter
387: * @return a String representing the single value of the parameter
388: *
389: * @since ostermillerutils 1.00.00
390: */
391: public String getParameter(String name) {
392: ArrayList<String> values = nameValuePairHash.get(name);
393: if (values == null) {
394: return null;
395: }
396: return (values.get(0));
397: }
398:
399: /**
400: * Returns an Enumeration of String objects containing the names of the
401: * parameters contained in this request. If the request has no parameters,
402: * the method returns an empty Enumeration.
403: *
404: * @return an Enumeration of String objects, each String containing the name
405: * of a request parameter; or an empty Enumeration if the request has
406: * no parameters
407: *
408: * @since ostermillerutils 1.00.00
409: */
410: public Enumeration<String> getParameterNames() {
411: return new IteratorEnumeration<String>(nameValuePairHash
412: .keySet().iterator());
413: }
414:
415: /**
416: * Returns the names of the
417: * parameters contained in this request. If the request has no parameters,
418: * the method returns an empty String array. Each name will appear only
419: * once, even if it was contained in the request multiple times. The order
420: * of the names may not match the order from the request.
421: *
422: * @return An array of Strings, each of which is the name
423: * of a request parameter; or an array of length zero if the request has
424: * no parameters
425: *
426: * @since ostermillerutils 1.03.00
427: */
428: public String[] getParameterNameList() {
429: return nameValuePairHash.keySet().toArray(new String[0]);
430: }
431:
432: /**
433: * Get the all the parameters in the order in which they were added.
434: *
435: * @return array of all name value pairs.
436: */
437: public NameValuePair[] getParameters() {
438: return nameValuePairList.toArray(new NameValuePair[0]);
439: }
440:
441: /**
442: * Returns the name value pairs properly escaped and written in URL format.
443: *
444: * @param enc Character encoding to use when escaping characters.
445: * @return URLEncoded name value pairs.
446: * @throws UnsupportedEncodingException If the named encoding is not supported.
447: *
448: * @since ostermillerutils 1.00.00
449: */
450: public String toString(String enc)
451: throws UnsupportedEncodingException {
452: StringBuffer sb = new StringBuffer();
453: boolean bFirst = true;
454: Iterator<NameValuePair> listIterator = nameValuePairList
455: .iterator();
456: while (listIterator.hasNext()) {
457: NameValuePair nameValuePair = listIterator.next();
458: if (!bFirst)
459: sb.append('&');
460: bFirst = false;
461: sb.append(nameValuePair.toString(enc));
462: }
463: return sb.toString();
464: }
465:
466: /**
467: * Returns the name value pairs properly escaped and written in URL format
468: * with UTF-8 URL encoding.
469: *
470: * @return URLEncoded name value pairs.
471: *
472: * @since ostermillerutils 1.03.00
473: */
474: @Override
475: public String toString() {
476: try {
477: return toString("UTF-8");
478: } catch (UnsupportedEncodingException uex) {
479: // UTF-8 should be supported
480: throw new RuntimeException(uex);
481: }
482: }
483: }
|