KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > util > UrlEncoded


1 // ========================================================================
2
// $Id: UrlEncoded.java,v 1.24 2005/12/21 23:14:38 gregwilkins Exp $
3
// Copyright 1999-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.util;
17
18 import java.io.UnsupportedEncodingException JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.commons.logging.Log;
23 import org.mortbay.log.LogFactory;
24
25 /* ------------------------------------------------------------ */
26 /** Handles coding of MIME "x-www-form-urlencoded".
27  * This class handles the encoding and decoding for either
28  * the query string of a URL or the content of a POST HTTP request.
29  *
30  * <p><h4>Notes</h4>
31  * The hashtable either contains String single values, vectors
32  * of String or arrays of Strings.
33  *
34  * This class is only partially synchronised. In particular, simple
35  * get operations are not protected from concurrent updates.
36  *
37  * @see java.net.URLEncoder
38  * @version $Id: UrlEncoded.java,v 1.24 2005/12/21 23:14:38 gregwilkins Exp $
39  * @author Greg Wilkins (gregw)
40  */

41 public class UrlEncoded extends MultiMap
42 {
43     private static Log log = LogFactory.getLog(UrlEncoded.class);
44
45     /* ----------------------------------------------------------------- */
46     public UrlEncoded(UrlEncoded url)
47     {
48         super(url);
49     }
50     
51     /* ----------------------------------------------------------------- */
52     public UrlEncoded()
53     {
54         super(6);
55     }
56     
57     /* ----------------------------------------------------------------- */
58     public UrlEncoded(String JavaDoc s)
59     {
60         super(6);
61         decode(s,StringUtil.__ISO_8859_1);
62     }
63     
64     /* ----------------------------------------------------------------- */
65     public UrlEncoded(String JavaDoc s, String JavaDoc charset)
66     {
67         super(6);
68         decode(s,charset);
69     }
70     
71     /* ----------------------------------------------------------------- */
72     public void decode(String JavaDoc query)
73     {
74         decodeTo(query,this,StringUtil.__ISO_8859_1);
75     }
76     
77     /* ----------------------------------------------------------------- */
78     public void decode(String JavaDoc query,String JavaDoc charset)
79     {
80         decodeTo(query,this,charset);
81     }
82     
83     /* -------------------------------------------------------------- */
84     /** Encode Hashtable with % encoding.
85      */

86     public String JavaDoc encode()
87     {
88         return encode(StringUtil.__ISO_8859_1,false);
89     }
90     
91     /* -------------------------------------------------------------- */
92     /** Encode Hashtable with % encoding.
93      */

94     public String JavaDoc encode(String JavaDoc charset)
95     {
96         return encode(charset,false);
97     }
98     
99     /* -------------------------------------------------------------- */
100     /** Encode Hashtable with % encoding.
101      * @param equalsForNullValue if True, then an '=' is always used, even
102      * for parameters without a value. e.g. "blah?a=&b=&c=".
103      */

104     public synchronized String JavaDoc encode(String JavaDoc charset, boolean equalsForNullValue)
105     {
106         if (charset==null)
107             charset=StringUtil.__ISO_8859_1;
108         
109         StringBuffer JavaDoc result = new StringBuffer JavaDoc(128);
110         synchronized(result)
111         {
112             Iterator JavaDoc iter = entrySet().iterator();
113             while(iter.hasNext())
114             {
115                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc)iter.next();
116                 
117                 String JavaDoc key = entry.getKey().toString();
118                 Object JavaDoc list = entry.getValue();
119                 int s=LazyList.size(list);
120                 
121                 if (s==0)
122                 {
123                     result.append(encodeString(key,charset));
124                     if(equalsForNullValue)
125                         result.append('=');
126                 }
127                 else
128                 {
129                     for (int i=0;i<s;i++)
130                     {
131                         if (i>0)
132                             result.append('&');
133                         Object JavaDoc val=LazyList.get(list,i);
134                         result.append(encodeString(key,charset));
135
136                         if (val!=null)
137                         {
138                             String JavaDoc str=val.toString();
139                             if (str.length()>0)
140                             {
141                                 result.append('=');
142                                 result.append(encodeString(str,charset));
143                             }
144                             else if (equalsForNullValue)
145                                 result.append('=');
146                         }
147                         else if (equalsForNullValue)
148                             result.append('=');
149                     }
150                 }
151                 if (iter.hasNext())
152                     result.append('&');
153             }
154             return result.toString();
155         }
156     }
157
158     /* -------------------------------------------------------------- */
159     /* Decoded parameters to Map.
160      * @param content the string containing the encoded parameters
161      * @param url The dictionary to add the parameters to
162      */

163     public static void decodeTo(String JavaDoc content,MultiMap map)
164     {
165         decodeTo(content,map,StringUtil.__ISO_8859_1);
166     }
167     
168
169
170     /* -------------------------------------------------------------- */
171     /** Decoded parameters to Map.
172      * @param content the string containing the encoded parameters
173      */

174     public static void decodeTo(String JavaDoc content, MultiMap map, String JavaDoc charset)
175     {
176         if (charset==null)
177             charset=StringUtil.__ISO_8859_1;
178
179         synchronized(map)
180         {
181             String JavaDoc key = null;
182             String JavaDoc value = null;
183             int mark=-1;
184             boolean encoded=false;
185             for (int i=0;i<content.length();i++)
186             {
187                 char c = content.charAt(i);
188                 switch (c)
189                 {
190                   case '&':
191                       value = encoded
192                           ?decodeString(content,mark+1,i-mark-1,charset)
193                           :content.substring(mark+1,i);
194                       
195                       mark=i;
196                       encoded=false;
197                       if (key != null)
198                       {
199                           map.add(key,value);
200                           key = null;
201                       }
202                       break;
203                   case '=':
204                       if (key!=null)
205                           break;
206                       key = encoded
207                           ?decodeString(content,mark+1,i-mark-1,charset)
208                           :content.substring(mark+1,i);
209                       mark=i;
210                       encoded=false;
211                       break;
212                   case '+':
213                       encoded=true;
214                       break;
215                   case '%':
216                       encoded=true;
217                       break;
218                 }
219             }
220             
221             if (key != null)
222             {
223                 value = encoded
224                     ?decodeString(content,mark+1,content.length()-mark-1,charset)
225                     :content.substring(mark+1);
226                 map.add(key,value);
227             }
228             else if (mark<content.length())
229             {
230                 key = encoded
231                     ?decodeString(content,mark+1,content.length()-mark-1,charset)
232                     :content.substring(mark+1);
233                 map.add(key,"");
234             }
235         }
236     }
237     
238     /* -------------------------------------------------------------- */
239     /** Decoded parameters to Map.
240      * @param data the byte[] containing the encoded parameters
241      */

242     public static void decodeTo(byte[] data, int offset, int length, MultiMap map, String JavaDoc charset)
243     {
244         if (data == null || length == 0)
245             return;
246
247         if (charset==null)
248             charset=StringUtil.__ISO_8859_1;
249         
250         synchronized(map)
251         {
252             try
253             {
254                 int ix = offset;
255                 int end = offset+length;
256                 int ox = offset;
257                 String JavaDoc key = null;
258                 String JavaDoc value = null;
259                 while (ix < end)
260                 {
261                     byte c = data[ix++];
262                     switch ((char) c)
263                     {
264                       case '&':
265                           value = new String JavaDoc(data, offset, ox, charset);
266                           if (key != null)
267                           {
268                               map.add(key,value);
269                               key = null;
270                           }
271                           ox = offset;
272                           break;
273                       case '=':
274                           if (key!=null)
275                               break;
276                           key = new String JavaDoc(data, offset, ox, charset);
277                           ox = offset;
278                           break;
279                       case '+':
280                           data[ox++] = (byte)' ';
281                           break;
282                       case '%':
283                           int i0 = (14<<4)+1;
284                           byte b0 = (byte)i0;
285                           data[ox++] = (byte)
286                               ((TypeUtil.convertHexDigit(data[ix++]) << 4)+
287                                TypeUtil.convertHexDigit(data[ix++]));
288                           break;
289                       default:
290                           data[ox++] = c;
291                     }
292                 }
293                 if (key != null)
294                 {
295                     value = new String JavaDoc(data, offset, ox, charset);
296                     map.add(key,value);
297                 }
298             }
299             catch(UnsupportedEncodingException JavaDoc e)
300             {
301                 log.warn(LogSupport.EXCEPTION,e);
302             }
303         }
304     }
305     
306     /* -------------------------------------------------------------- */
307     /** Decode String with % encoding.
308      * This method makes the assumption that the majority of calls
309      * will need no decoding and uses the 8859 encoding.
310      */

311     public static String JavaDoc decodeString(String JavaDoc encoded)
312     {
313         return decodeString(encoded,0,encoded.length(),StringUtil.__ISO_8859_1);
314     }
315     
316     /* -------------------------------------------------------------- */
317     /** Decode String with % encoding.
318      * This method makes the assumption that the majority of calls
319      * will need no decoding.
320      */

321     public static String JavaDoc decodeString(String JavaDoc encoded,String JavaDoc charset)
322     {
323         return decodeString(encoded,0,encoded.length(),charset);
324     }
325     
326             
327     /* -------------------------------------------------------------- */
328     /** Decode String with % encoding.
329      * This method makes the assumption that the majority of calls
330      * will need no decoding.
331      */

332     public static String JavaDoc decodeString(String JavaDoc encoded,int offset,int length,String JavaDoc charset)
333     {
334         if (charset==null)
335             charset=StringUtil.__ISO_8859_1;
336         byte[] bytes=null;
337         int n=0;
338         
339         for (int i=0;i<length;i++)
340         {
341             char c = encoded.charAt(offset+i);
342             if (c<0||c>0xff)
343                 throw new IllegalArgumentException JavaDoc("Not encoded");
344             
345             if (c=='+')
346             {
347                 if (bytes==null)
348                 {
349                     bytes=new byte[length*2];
350                     encoded.getBytes(offset, offset+i, bytes, 0);
351                     n=i;
352                 }
353                 bytes[n++] = (byte) ' ';
354             }
355             else if (c=='%' && (i+2)<length)
356             {
357                 byte b;
358                 char cn = encoded.charAt(offset+i+1);
359                 if (cn>='a' && cn<='z')
360                     b=(byte)(10+cn-'a');
361                 else if (cn>='A' && cn<='Z')
362                     b=(byte)(10+cn-'A');
363                 else
364                     b=(byte)(cn-'0');
365                 cn = encoded.charAt(offset+i+2);
366                 if (cn>='a' && cn<='z')
367                     b=(byte)(b*16+10+cn-'a');
368                 else if (cn>='A' && cn<='Z')
369                     b=(byte)(b*16+10+cn-'A');
370                 else
371                     b=(byte)(b*16+cn-'0');
372
373                 if (bytes==null)
374                 {
375                     bytes=new byte[length*2];
376                     encoded.getBytes(offset, offset+i, bytes, 0);
377                     n=i;
378                 }
379                 i+=2;
380                 bytes[n++]=b;
381             }
382             else if (n>0)
383                 bytes[n++] = (byte) c;
384         }
385
386         if (bytes==null)
387         {
388             if (offset==0 && encoded.length()==length)
389                 return encoded;
390             return encoded.substring(offset,offset+length);
391         }
392         
393         try
394         {
395             return new String JavaDoc(bytes,0,n,charset);
396         }
397         catch (UnsupportedEncodingException JavaDoc e)
398         {
399             return new String JavaDoc(bytes,0,n);
400         }
401         
402     }
403     
404     /* ------------------------------------------------------------ */
405     /** Perform URL encoding.
406      * Assumes 8859 charset
407      * @param string
408      * @return encoded string.
409      */

410     public static String JavaDoc encodeString(String JavaDoc string)
411     {
412         return encodeString(string,StringUtil.__ISO_8859_1);
413     }
414     
415     /* ------------------------------------------------------------ */
416     /** Perform URL encoding.
417      * @param string
418      * @return encoded string.
419      */

420     public static String JavaDoc encodeString(String JavaDoc string,String JavaDoc charset)
421     {
422         if (charset==null)
423             charset=StringUtil.__ISO_8859_1;
424         byte[] bytes=null;
425         try
426         {
427             bytes=string.getBytes(charset);
428         }
429         catch(UnsupportedEncodingException JavaDoc e)
430         {
431             log.warn(LogSupport.EXCEPTION,e);
432             bytes=string.getBytes();
433         }
434         
435         int len=bytes.length;
436         byte[] encoded= new byte[bytes.length*3];
437         int n=0;
438         boolean noEncode=true;
439         
440         for (int i=0;i<len;i++)
441         {
442             byte b = bytes[i];
443             
444             if (b==' ')
445             {
446                 noEncode=false;
447                 encoded[n++]=(byte)'+';
448             }
449             else if (b>='a' && b<='z' ||
450                      b>='A' && b<='Z' ||
451                      b>='0' && b<='9')
452             {
453                 encoded[n++]=b;
454             }
455             else
456             {
457                 noEncode=false;
458                 encoded[n++]=(byte)'%';
459                 byte nibble= (byte) ((b&0xf0)>>4);
460                 if (nibble>=10)
461                     encoded[n++]=(byte)('A'+nibble-10);
462                 else
463                     encoded[n++]=(byte)('0'+nibble);
464                 nibble= (byte) (b&0xf);
465                 if (nibble>=10)
466                     encoded[n++]=(byte)('A'+nibble-10);
467                 else
468                     encoded[n++]=(byte)('0'+nibble);
469             }
470         }
471
472         if (noEncode)
473             return string;
474         
475         try
476         {
477             return new String JavaDoc(encoded,0,n,charset);
478         }
479         catch(UnsupportedEncodingException JavaDoc e)
480         {
481             log.warn(LogSupport.EXCEPTION,e);
482             return new String JavaDoc(encoded,0,n);
483         }
484     }
485
486
487     /* ------------------------------------------------------------ */
488     /**
489      */

490     public Object JavaDoc clone()
491     {
492     return super.clone();
493     }
494 }
495
Popular Tags