001: /*
002: * @(#)URLEncoding.java 1.2 04/12/06
003: *
004: * Copyright (c) 1997-2004 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package org.pnuts.net;
010:
011: import java.util.*;
012: import java.io.*;
013:
014: /**
015: * A set of utility methods which are related to character encoding.
016: *
017: * @version 1.1
018: */
019: public class URLEncoding {
020: private final static boolean DEBUG = false;
021: private static BitSet dontNeedEncoding;
022: private final static int caseDiff = ('a' - 'A');
023:
024: static {
025: dontNeedEncoding = new BitSet(256);
026: int i;
027: for (i = 'a'; i <= 'z'; i++) {
028: dontNeedEncoding.set(i);
029: }
030: for (i = 'A'; i <= 'Z'; i++) {
031: dontNeedEncoding.set(i);
032: }
033: for (i = '0'; i <= '9'; i++) {
034: dontNeedEncoding.set(i);
035: }
036: dontNeedEncoding.set(' '); /* encoding a space to a + is done in the encode() method */
037: dontNeedEncoding.set('-');
038: dontNeedEncoding.set('_');
039: dontNeedEncoding.set('.');
040: dontNeedEncoding.set('*');
041: }
042:
043: protected URLEncoding() {
044: }
045:
046: /**
047: * Translates a string into <code>x-www-form-urlencoded</code> format.
048: *
049: * @param s The input data to be encoded
050: * @param enc The character encoding
051: */
052: public static String encode(String s, String enc)
053: throws UnsupportedEncodingException {
054: int maxBytesPerChar = 10;
055: StringBuffer out = new StringBuffer();
056: ByteArrayOutputStream buf = new ByteArrayOutputStream(
057: maxBytesPerChar);
058: OutputStreamWriter writer = new OutputStreamWriter(buf, enc);
059:
060: for (int i = 0; i < s.length(); i++) {
061: int c = (int) s.charAt(i);
062: if (dontNeedEncoding.get(c)) {
063: if (c == ' ') {
064: c = '+';
065: }
066: out.append((char) c);
067: } else {
068: // convert to external encoding before hex conversion
069: try {
070: writer.write(c);
071: writer.flush();
072: } catch (IOException e) {
073: buf.reset();
074: continue;
075: }
076: byte[] ba = buf.toByteArray();
077: for (int j = 0; j < ba.length; j++) {
078: out.append('%');
079: char ch = Character
080: .forDigit((ba[j] >> 4) & 0xF, 16);
081: // converting to use uppercase letter as part of
082: // the hex value if ch is a letter.
083: if (Character.isLetter(ch)) {
084: ch -= caseDiff;
085: }
086: out.append(ch);
087: ch = Character.forDigit(ba[j] & 0xF, 16);
088: if (Character.isLetter(ch)) {
089: ch -= caseDiff;
090: }
091: out.append(ch);
092: }
093: buf.reset();
094: }
095: }
096:
097: return out.toString();
098: }
099:
100: /**
101: * Decodes a <code>x-www-form-urlencoded</code> to a String.
102: *
103: * @param s The encoded data to be decoded
104: * @param enc The character encoding
105: */
106: public static String decode(String s, String enc)
107: throws UnsupportedEncodingException {
108: ByteArrayOutputStream bout = new ByteArrayOutputStream();
109: for (int i = 0; i < s.length(); i++) {
110: char c = s.charAt(i);
111: switch (c) {
112: case '+':
113: bout.write((int) ' ');
114: break;
115: case '%':
116: try {
117: bout.write(Integer.parseInt(s.substring(i + 1,
118: i + 3), 16));
119: } catch (NumberFormatException e) {
120: throw new IllegalArgumentException(s.substring(
121: i + 1, i + 3));
122: }
123: i += 2;
124: break;
125: default:
126: bout.write((int) c);
127: break;
128: }
129: }
130: return new String(bout.toByteArray(), enc);
131: }
132:
133: /**
134: * Parses a QUERY_STRING passed from the client to the server and build a Hashtable
135: * with key-value pairs.
136: *
137: * @param s "QUERY_STRING"
138: * @param enc The character encoding
139: *
140: * @see javax.servlet.http.HttpUtils#parseQueryString(java.lang.String)
141: */
142: public static Map parseQueryString(String s, String enc)
143: throws UnsupportedEncodingException {
144: if (DEBUG) {
145: System.out.println("parseQueryString(" + s + ", " + enc
146: + ")");
147: }
148: String valArray[] = null;
149:
150: if (s == null) {
151: throw new IllegalArgumentException();
152: }
153: Hashtable ht = new Hashtable();
154: StringBuffer sb = new StringBuffer();
155: StringTokenizer st = new StringTokenizer(s, "&");
156: while (st.hasMoreTokens()) {
157: String pair = (String) st.nextToken();
158: int pos = pair.indexOf('=');
159: if (pos == -1) {
160: continue;
161: }
162: String key = decode(pair.substring(0, pos), enc);
163: String val = decode(pair.substring(pos + 1, pair.length()),
164: enc);
165: if (DEBUG) {
166: System.out.println("key = " + key + ", value = " + val);
167: }
168:
169: if (ht.containsKey(key)) {
170: String oldVals[] = (String[]) ht.get(key);
171: valArray = new String[oldVals.length + 1];
172: for (int i = 0; i < oldVals.length; i++)
173: valArray[i] = oldVals[i];
174: valArray[oldVals.length] = val;
175: } else {
176: valArray = new String[1];
177: valArray[0] = val;
178: }
179: ht.put(key, valArray);
180: }
181: return ht;
182: }
183:
184: /**
185: * Makes a QUERY STRING from java.util.Map.
186: *
187: * @param table a java.util.Map object
188: * @param enc the character encoding
189: *
190: * @return the resulting query string.
191: */
192: public static String makeQueryString(Map table, String enc)
193: throws UnsupportedEncodingException {
194: boolean first = true;
195: StringBuffer buf = new StringBuffer();
196: for (Iterator it = table.keySet().iterator(); it.hasNext();) {
197: String key = (String) it.next();
198: Object value = table.get(key);
199: if (value instanceof Object[]) {
200: Object[] avalue = (Object[]) value;
201: for (int i = 0; i < avalue.length; i++) {
202: String svalue = String.valueOf(avalue[i]);
203: if (svalue == null)
204: continue;
205: if (first) {
206: first = false;
207: } else {
208: buf.append("&");
209: }
210: if (value != null) {
211: buf.append(encode(key, enc) + "="
212: + encode(svalue, enc));
213: }
214: }
215: } else if (value instanceof String) {
216: String svalue = (String) value;
217: if (first) {
218: first = false;
219: } else {
220: buf.append("&");
221: }
222: if (value != null) {
223: buf.append(encode(key, enc) + "="
224: + encode(svalue, enc));
225: }
226: }
227: }
228: return buf.toString();
229: }
230: }
|