comparison src/org/eclipse/jetty/util/UrlEncoded.java @ 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents
children 8e9db0bbf4f9
comparison
equal deleted inserted replaced
801:6a21393191c1 802:3428c60d7cfc
1 //
2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import static org.eclipse.jetty.util.TypeUtil.convertHexDigit;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.StringWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.util.Iterator;
29 import java.util.Map;
30
31 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34
35 /* ------------------------------------------------------------ */
36 /** Handles coding of MIME "x-www-form-urlencoded".
37 * <p>
38 * This class handles the encoding and decoding for either
39 * the query string of a URL or the _content of a POST HTTP request.
40 *
41 * <h4>Notes</h4>
42 * The UTF-8 charset is assumed, unless otherwise defined by either
43 * passing a parameter or setting the "org.eclipse.jetty.util.UrlEncoding.charset"
44 * System property.
45 * <p>
46 * The hashtable either contains String single values, vectors
47 * of String or arrays of Strings.
48 * <p>
49 * This class is only partially synchronised. In particular, simple
50 * get operations are not protected from concurrent updates.
51 *
52 * @see java.net.URLEncoder
53 */
54 public class UrlEncoded extends MultiMap implements Cloneable
55 {
56 private static final Logger LOG = Log.getLogger(UrlEncoded.class);
57
58 public static final String ENCODING = System.getProperty("org.eclipse.jetty.util.UrlEncoding.charset",StringUtil.__UTF8);
59
60 /* ----------------------------------------------------------------- */
61 public UrlEncoded(UrlEncoded url)
62 {
63 super(url);
64 }
65
66 /* ----------------------------------------------------------------- */
67 public UrlEncoded()
68 {
69 super(6);
70 }
71
72 /* ----------------------------------------------------------------- */
73 public UrlEncoded(String s)
74 {
75 super(6);
76 decode(s,ENCODING);
77 }
78
79 /* ----------------------------------------------------------------- */
80 public UrlEncoded(String s, String charset)
81 {
82 super(6);
83 decode(s,charset);
84 }
85
86 /* ----------------------------------------------------------------- */
87 public void decode(String query)
88 {
89 decodeTo(query,this,ENCODING,-1);
90 }
91
92 /* ----------------------------------------------------------------- */
93 public void decode(String query,String charset)
94 {
95 decodeTo(query,this,charset,-1);
96 }
97
98 /* -------------------------------------------------------------- */
99 /** Encode Hashtable with % encoding.
100 */
101 public String encode()
102 {
103 return encode(ENCODING,false);
104 }
105
106 /* -------------------------------------------------------------- */
107 /** Encode Hashtable with % encoding.
108 */
109 public String encode(String charset)
110 {
111 return encode(charset,false);
112 }
113
114 /* -------------------------------------------------------------- */
115 /** Encode Hashtable with % encoding.
116 * @param equalsForNullValue if True, then an '=' is always used, even
117 * for parameters without a value. e.g. "blah?a=&b=&c=".
118 */
119 public synchronized String encode(String charset, boolean equalsForNullValue)
120 {
121 return encode(this,charset,equalsForNullValue);
122 }
123
124 /* -------------------------------------------------------------- */
125 /** Encode Hashtable with % encoding.
126 * @param equalsForNullValue if True, then an '=' is always used, even
127 * for parameters without a value. e.g. "blah?a=&b=&c=".
128 */
129 public static String encode(MultiMap map, String charset, boolean equalsForNullValue)
130 {
131 if (charset==null)
132 charset=ENCODING;
133
134 StringBuilder result = new StringBuilder(128);
135
136 Iterator iter = map.entrySet().iterator();
137 while(iter.hasNext())
138 {
139 Map.Entry entry = (Map.Entry)iter.next();
140
141 String key = entry.getKey().toString();
142 Object list = entry.getValue();
143 int s=LazyList.size(list);
144
145 if (s==0)
146 {
147 result.append(encodeString(key,charset));
148 if(equalsForNullValue)
149 result.append('=');
150 }
151 else
152 {
153 for (int i=0;i<s;i++)
154 {
155 if (i>0)
156 result.append('&');
157 Object val=LazyList.get(list,i);
158 result.append(encodeString(key,charset));
159
160 if (val!=null)
161 {
162 String str=val.toString();
163 if (str.length()>0)
164 {
165 result.append('=');
166 result.append(encodeString(str,charset));
167 }
168 else if (equalsForNullValue)
169 result.append('=');
170 }
171 else if (equalsForNullValue)
172 result.append('=');
173 }
174 }
175 if (iter.hasNext())
176 result.append('&');
177 }
178 return result.toString();
179 }
180
181
182
183 /* -------------------------------------------------------------- */
184 /** Decoded parameters to Map.
185 * @param content the string containing the encoded parameters
186 */
187 public static void decodeTo(String content, MultiMap map, String charset)
188 {
189 decodeTo(content,map,charset,-1);
190 }
191
192 /* -------------------------------------------------------------- */
193 /** Decoded parameters to Map.
194 * @param content the string containing the encoded parameters
195 */
196 public static void decodeTo(String content, MultiMap map, String charset, int maxKeys)
197 {
198 if (charset==null)
199 charset=ENCODING;
200
201 synchronized(map)
202 {
203 String key = null;
204 String value = null;
205 int mark=-1;
206 boolean encoded=false;
207 for (int i=0;i<content.length();i++)
208 {
209 char c = content.charAt(i);
210 switch (c)
211 {
212 case '&':
213 int l=i-mark-1;
214 value = l==0?"":
215 (encoded?decodeString(content,mark+1,l,charset):content.substring(mark+1,i));
216 mark=i;
217 encoded=false;
218 if (key != null)
219 {
220 map.add(key,value);
221 }
222 else if (value!=null&&value.length()>0)
223 {
224 map.add(value,"");
225 }
226 key = null;
227 value=null;
228 if (maxKeys>0 && map.size()>maxKeys)
229 throw new IllegalStateException("Form too many keys");
230 break;
231 case '=':
232 if (key!=null)
233 break;
234 key = encoded?decodeString(content,mark+1,i-mark-1,charset):content.substring(mark+1,i);
235 mark=i;
236 encoded=false;
237 break;
238 case '+':
239 encoded=true;
240 break;
241 case '%':
242 encoded=true;
243 break;
244 }
245 }
246
247 if (key != null)
248 {
249 int l=content.length()-mark-1;
250 value = l==0?"":(encoded?decodeString(content,mark+1,l,charset):content.substring(mark+1));
251 map.add(key,value);
252 }
253 else if (mark<content.length())
254 {
255 key = encoded
256 ?decodeString(content,mark+1,content.length()-mark-1,charset)
257 :content.substring(mark+1);
258 if (key != null && key.length() > 0)
259 {
260 map.add(key,"");
261 }
262 }
263 }
264 }
265
266 /* -------------------------------------------------------------- */
267 /** Decoded parameters to Map.
268 * @param raw the byte[] containing the encoded parameters
269 * @param offset the offset within raw to decode from
270 * @param length the length of the section to decode
271 * @param map the {@link MultiMap} to populate
272 */
273 public static void decodeUtf8To(byte[] raw,int offset, int length, MultiMap map)
274 {
275 decodeUtf8To(raw,offset,length,map,new Utf8StringBuilder());
276 }
277
278 /* -------------------------------------------------------------- */
279 /** Decoded parameters to Map.
280 * @param raw the byte[] containing the encoded parameters
281 * @param offset the offset within raw to decode from
282 * @param length the length of the section to decode
283 * @param map the {@link MultiMap} to populate
284 * @param buffer the buffer to decode into
285 */
286 public static void decodeUtf8To(byte[] raw,int offset, int length, MultiMap map,Utf8StringBuilder buffer)
287 {
288 synchronized(map)
289 {
290 String key = null;
291 String value = null;
292
293 // TODO cache of parameter names ???
294 int end=offset+length;
295 for (int i=offset;i<end;i++)
296 {
297 byte b=raw[i];
298 try
299 {
300 switch ((char)(0xff&b))
301 {
302 case '&':
303 value = buffer.length()==0?"":buffer.toString();
304 buffer.reset();
305 if (key != null)
306 {
307 map.add(key,value);
308 }
309 else if (value!=null&&value.length()>0)
310 {
311 map.add(value,"");
312 }
313 key = null;
314 value=null;
315 break;
316
317 case '=':
318 if (key!=null)
319 {
320 buffer.append(b);
321 break;
322 }
323 key = buffer.toString();
324 buffer.reset();
325 break;
326
327 case '+':
328 buffer.append((byte)' ');
329 break;
330
331 case '%':
332 if (i+2<end)
333 {
334 if ('u'==raw[i+1])
335 {
336 i++;
337 if (i+4<end)
338 buffer.getStringBuilder().append(Character.toChars((convertHexDigit(raw[++i])<<12) +(convertHexDigit(raw[++i])<<8) + (convertHexDigit(raw[++i])<<4) +convertHexDigit(raw[++i])));
339 else
340 {
341 buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
342 i=end;
343 }
344 }
345 else
346 buffer.append((byte)((convertHexDigit(raw[++i])<<4) + convertHexDigit(raw[++i])));
347 }
348 else
349 {
350 buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
351 i=end;
352 }
353 break;
354
355 default:
356 buffer.append(b);
357 break;
358 }
359 }
360 catch(NotUtf8Exception e)
361 {
362 LOG.warn(e.toString());
363 LOG.debug(e);
364 }
365 }
366
367 if (key != null)
368 {
369 value = buffer.length()==0?"":buffer.toReplacedString();
370 buffer.reset();
371 map.add(key,value);
372 }
373 else if (buffer.length()>0)
374 {
375 map.add(buffer.toReplacedString(),"");
376 }
377 }
378 }
379
380 /* -------------------------------------------------------------- */
381 /** Decoded parameters to Map.
382 * @param in InputSteam to read
383 * @param map MultiMap to add parameters to
384 * @param maxLength maximum number of keys to read or -1 for no limit
385 */
386 public static void decode88591To(InputStream in, MultiMap map, int maxLength, int maxKeys)
387 throws IOException
388 {
389 synchronized(map)
390 {
391 StringBuffer buffer = new StringBuffer();
392 String key = null;
393 String value = null;
394
395 int b;
396
397 // TODO cache of parameter names ???
398 int totalLength=0;
399 while ((b=in.read())>=0)
400 {
401 switch ((char) b)
402 {
403 case '&':
404 value = buffer.length()==0?"":buffer.toString();
405 buffer.setLength(0);
406 if (key != null)
407 {
408 map.add(key,value);
409 }
410 else if (value!=null&&value.length()>0)
411 {
412 map.add(value,"");
413 }
414 key = null;
415 value=null;
416 if (maxKeys>0 && map.size()>maxKeys)
417 throw new IllegalStateException("Form too many keys");
418 break;
419
420 case '=':
421 if (key!=null)
422 {
423 buffer.append((char)b);
424 break;
425 }
426 key = buffer.toString();
427 buffer.setLength(0);
428 break;
429
430 case '+':
431 buffer.append(' ');
432 break;
433
434 case '%':
435 int code0=in.read();
436 if ('u'==code0)
437 {
438 int code1=in.read();
439 if (code1>=0)
440 {
441 int code2=in.read();
442 if (code2>=0)
443 {
444 int code3=in.read();
445 if (code3>=0)
446 buffer.append(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
447 }
448 }
449 }
450 else if (code0>=0)
451 {
452 int code1=in.read();
453 if (code1>=0)
454 buffer.append((char)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
455 }
456 break;
457
458 default:
459 buffer.append((char)b);
460 break;
461 }
462 if (maxLength>=0 && (++totalLength > maxLength))
463 throw new IllegalStateException("Form too large");
464 }
465
466 if (key != null)
467 {
468 value = buffer.length()==0?"":buffer.toString();
469 buffer.setLength(0);
470 map.add(key,value);
471 }
472 else if (buffer.length()>0)
473 {
474 map.add(buffer.toString(), "");
475 }
476 }
477 }
478
479 /* -------------------------------------------------------------- */
480 /** Decoded parameters to Map.
481 * @param in InputSteam to read
482 * @param map MultiMap to add parameters to
483 * @param maxLength maximum number of keys to read or -1 for no limit
484 */
485 public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength, int maxKeys)
486 throws IOException
487 {
488 synchronized(map)
489 {
490 Utf8StringBuilder buffer = new Utf8StringBuilder();
491 String key = null;
492 String value = null;
493
494 int b;
495
496 // TODO cache of parameter names ???
497 int totalLength=0;
498 while ((b=in.read())>=0)
499 {
500 try
501 {
502 switch ((char) b)
503 {
504 case '&':
505 value = buffer.length()==0?"":buffer.toString();
506 buffer.reset();
507 if (key != null)
508 {
509 map.add(key,value);
510 }
511 else if (value!=null&&value.length()>0)
512 {
513 map.add(value,"");
514 }
515 key = null;
516 value=null;
517 if (maxKeys>0 && map.size()>maxKeys)
518 throw new IllegalStateException("Form too many keys");
519 break;
520
521 case '=':
522 if (key!=null)
523 {
524 buffer.append((byte)b);
525 break;
526 }
527 key = buffer.toString();
528 buffer.reset();
529 break;
530
531 case '+':
532 buffer.append((byte)' ');
533 break;
534
535 case '%':
536 int code0=in.read();
537 if ('u'==code0)
538 {
539 int code1=in.read();
540 if (code1>=0)
541 {
542 int code2=in.read();
543 if (code2>=0)
544 {
545 int code3=in.read();
546 if (code3>=0)
547 buffer.getStringBuilder().append(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
548 }
549 }
550 }
551 else if (code0>=0)
552 {
553 int code1=in.read();
554 if (code1>=0)
555 buffer.append((byte)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
556 }
557 break;
558
559 default:
560 buffer.append((byte)b);
561 break;
562 }
563 }
564 catch(NotUtf8Exception e)
565 {
566 LOG.warn(e.toString());
567 LOG.debug(e);
568 }
569 if (maxLength>=0 && (++totalLength > maxLength))
570 throw new IllegalStateException("Form too large");
571 }
572
573 if (key != null)
574 {
575 value = buffer.length()==0?"":buffer.toString();
576 buffer.reset();
577 map.add(key,value);
578 }
579 else if (buffer.length()>0)
580 {
581 map.add(buffer.toString(), "");
582 }
583 }
584 }
585
586 /* -------------------------------------------------------------- */
587 public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength, int maxKeys) throws IOException
588 {
589 InputStreamReader input = new InputStreamReader(in,StringUtil.__UTF16);
590 StringWriter buf = new StringWriter(8192);
591 IO.copy(input,buf,maxLength);
592
593 decodeTo(buf.getBuffer().toString(),map,StringUtil.__UTF16,maxKeys);
594 }
595
596 /* -------------------------------------------------------------- */
597 /** Decoded parameters to Map.
598 * @param in the stream containing the encoded parameters
599 */
600 public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength, int maxKeys)
601 throws IOException
602 {
603 //no charset present, use the configured default
604 if (charset==null)
605 {
606 charset=ENCODING;
607 }
608
609 if (StringUtil.__UTF8.equalsIgnoreCase(charset))
610 {
611 decodeUtf8To(in,map,maxLength,maxKeys);
612 return;
613 }
614
615 if (StringUtil.__ISO_8859_1.equals(charset))
616 {
617 decode88591To(in,map,maxLength,maxKeys);
618 return;
619 }
620
621 if (StringUtil.__UTF16.equalsIgnoreCase(charset)) // Should be all 2 byte encodings
622 {
623 decodeUtf16To(in,map,maxLength,maxKeys);
624 return;
625 }
626
627
628 synchronized(map)
629 {
630 String key = null;
631 String value = null;
632
633 int c;
634
635 int totalLength = 0;
636 ByteArrayOutputStream2 output = new ByteArrayOutputStream2();
637
638 int size=0;
639
640 while ((c=in.read())>0)
641 {
642 switch ((char) c)
643 {
644 case '&':
645 size=output.size();
646 value = size==0?"":output.toString(charset);
647 output.setCount(0);
648 if (key != null)
649 {
650 map.add(key,value);
651 }
652 else if (value!=null&&value.length()>0)
653 {
654 map.add(value,"");
655 }
656 key = null;
657 value=null;
658 if (maxKeys>0 && map.size()>maxKeys)
659 throw new IllegalStateException("Form too many keys");
660 break;
661 case '=':
662 if (key!=null)
663 {
664 output.write(c);
665 break;
666 }
667 size=output.size();
668 key = size==0?"":output.toString(charset);
669 output.setCount(0);
670 break;
671 case '+':
672 output.write(' ');
673 break;
674 case '%':
675 int code0=in.read();
676 if ('u'==code0)
677 {
678 int code1=in.read();
679 if (code1>=0)
680 {
681 int code2=in.read();
682 if (code2>=0)
683 {
684 int code3=in.read();
685 if (code3>=0)
686 output.write(new String(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3))).getBytes(charset));
687 }
688 }
689
690 }
691 else if (code0>=0)
692 {
693 int code1=in.read();
694 if (code1>=0)
695 output.write((convertHexDigit(code0)<<4)+convertHexDigit(code1));
696 }
697 break;
698 default:
699 output.write(c);
700 break;
701 }
702
703 totalLength++;
704 if (maxLength>=0 && totalLength > maxLength)
705 throw new IllegalStateException("Form too large");
706 }
707
708 size=output.size();
709 if (key != null)
710 {
711 value = size==0?"":output.toString(charset);
712 output.setCount(0);
713 map.add(key,value);
714 }
715 else if (size>0)
716 map.add(output.toString(charset),"");
717 }
718 }
719
720 /* -------------------------------------------------------------- */
721 /** Decode String with % encoding.
722 * This method makes the assumption that the majority of calls
723 * will need no decoding.
724 */
725 public static String decodeString(String encoded,int offset,int length,String charset)
726 {
727 if (charset==null || StringUtil.isUTF8(charset))
728 {
729 Utf8StringBuffer buffer=null;
730
731 for (int i=0;i<length;i++)
732 {
733 char c = encoded.charAt(offset+i);
734 if (c<0||c>0xff)
735 {
736 if (buffer==null)
737 {
738 buffer=new Utf8StringBuffer(length);
739 buffer.getStringBuffer().append(encoded,offset,offset+i+1);
740 }
741 else
742 buffer.getStringBuffer().append(c);
743 }
744 else if (c=='+')
745 {
746 if (buffer==null)
747 {
748 buffer=new Utf8StringBuffer(length);
749 buffer.getStringBuffer().append(encoded,offset,offset+i);
750 }
751
752 buffer.getStringBuffer().append(' ');
753 }
754 else if (c=='%')
755 {
756 if (buffer==null)
757 {
758 buffer=new Utf8StringBuffer(length);
759 buffer.getStringBuffer().append(encoded,offset,offset+i);
760 }
761
762 if ((i+2)<length)
763 {
764 try
765 {
766 if ('u'==encoded.charAt(offset+i+1))
767 {
768 if((i+5)<length)
769 {
770 int o=offset+i+2;
771 i+=5;
772 String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
773 buffer.getStringBuffer().append(unicode);
774 }
775 else
776 {
777 i=length;
778 buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
779 }
780 }
781 else
782 {
783 int o=offset+i+1;
784 i+=2;
785 byte b=(byte)TypeUtil.parseInt(encoded,o,2,16);
786 buffer.append(b);
787 }
788 }
789 catch(NotUtf8Exception e)
790 {
791 LOG.warn(e.toString());
792 LOG.debug(e);
793 }
794 catch(NumberFormatException nfe)
795 {
796 LOG.debug(nfe);
797 buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
798 }
799 }
800 else
801 {
802 buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
803 i=length;
804 }
805 }
806 else if (buffer!=null)
807 buffer.getStringBuffer().append(c);
808 }
809
810 if (buffer==null)
811 {
812 if (offset==0 && encoded.length()==length)
813 return encoded;
814 return encoded.substring(offset,offset+length);
815 }
816
817 return buffer.toReplacedString();
818 }
819 else
820 {
821 StringBuffer buffer=null;
822
823 try
824 {
825 for (int i=0;i<length;i++)
826 {
827 char c = encoded.charAt(offset+i);
828 if (c<0||c>0xff)
829 {
830 if (buffer==null)
831 {
832 buffer=new StringBuffer(length);
833 buffer.append(encoded,offset,offset+i+1);
834 }
835 else
836 buffer.append(c);
837 }
838 else if (c=='+')
839 {
840 if (buffer==null)
841 {
842 buffer=new StringBuffer(length);
843 buffer.append(encoded,offset,offset+i);
844 }
845
846 buffer.append(' ');
847 }
848 else if (c=='%')
849 {
850 if (buffer==null)
851 {
852 buffer=new StringBuffer(length);
853 buffer.append(encoded,offset,offset+i);
854 }
855
856 byte[] ba=new byte[length];
857 int n=0;
858 while(c>=0 && c<=0xff)
859 {
860 if (c=='%')
861 {
862 if(i+2<length)
863 {
864 try
865 {
866 if ('u'==encoded.charAt(offset+i+1))
867 {
868 if (i+6<length)
869 {
870 int o=offset+i+2;
871 i+=6;
872 String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
873 byte[] reencoded = unicode.getBytes(charset);
874 System.arraycopy(reencoded,0,ba,n,reencoded.length);
875 n+=reencoded.length;
876 }
877 else
878 {
879 ba[n++] = (byte)'?';
880 i=length;
881 }
882 }
883 else
884 {
885 int o=offset+i+1;
886 i+=3;
887 ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
888 n++;
889 }
890 }
891 catch(NumberFormatException nfe)
892 {
893 LOG.ignore(nfe);
894 ba[n++] = (byte)'?';
895 }
896 }
897 else
898 {
899 ba[n++] = (byte)'?';
900 i=length;
901 }
902 }
903 else if (c=='+')
904 {
905 ba[n++]=(byte)' ';
906 i++;
907 }
908 else
909 {
910 ba[n++]=(byte)c;
911 i++;
912 }
913
914 if (i>=length)
915 break;
916 c = encoded.charAt(offset+i);
917 }
918
919 i--;
920 buffer.append(new String(ba,0,n,charset));
921
922 }
923 else if (buffer!=null)
924 buffer.append(c);
925 }
926
927 if (buffer==null)
928 {
929 if (offset==0 && encoded.length()==length)
930 return encoded;
931 return encoded.substring(offset,offset+length);
932 }
933
934 return buffer.toString();
935 }
936 catch (UnsupportedEncodingException e)
937 {
938 throw new RuntimeException(e);
939 }
940 }
941
942 }
943
944 /* ------------------------------------------------------------ */
945 /** Perform URL encoding.
946 * @param string
947 * @return encoded string.
948 */
949 public static String encodeString(String string)
950 {
951 return encodeString(string,ENCODING);
952 }
953
954 /* ------------------------------------------------------------ */
955 /** Perform URL encoding.
956 * @param string
957 * @return encoded string.
958 */
959 public static String encodeString(String string,String charset)
960 {
961 if (charset==null)
962 charset=ENCODING;
963 byte[] bytes=null;
964 try
965 {
966 bytes=string.getBytes(charset);
967 }
968 catch(UnsupportedEncodingException e)
969 {
970 // LOG.warn(LogSupport.EXCEPTION,e);
971 bytes=string.getBytes();
972 }
973
974 int len=bytes.length;
975 byte[] encoded= new byte[bytes.length*3];
976 int n=0;
977 boolean noEncode=true;
978
979 for (int i=0;i<len;i++)
980 {
981 byte b = bytes[i];
982
983 if (b==' ')
984 {
985 noEncode=false;
986 encoded[n++]=(byte)'+';
987 }
988 else if (b>='a' && b<='z' ||
989 b>='A' && b<='Z' ||
990 b>='0' && b<='9')
991 {
992 encoded[n++]=b;
993 }
994 else
995 {
996 noEncode=false;
997 encoded[n++]=(byte)'%';
998 byte nibble= (byte) ((b&0xf0)>>4);
999 if (nibble>=10)
1000 encoded[n++]=(byte)('A'+nibble-10);
1001 else
1002 encoded[n++]=(byte)('0'+nibble);
1003 nibble= (byte) (b&0xf);
1004 if (nibble>=10)
1005 encoded[n++]=(byte)('A'+nibble-10);
1006 else
1007 encoded[n++]=(byte)('0'+nibble);
1008 }
1009 }
1010
1011 if (noEncode)
1012 return string;
1013
1014 try
1015 {
1016 return new String(encoded,0,n,charset);
1017 }
1018 catch(UnsupportedEncodingException e)
1019 {
1020 // LOG.warn(LogSupport.EXCEPTION,e);
1021 return new String(encoded,0,n);
1022 }
1023 }
1024
1025
1026 /* ------------------------------------------------------------ */
1027 /**
1028 */
1029 @Override
1030 public Object clone()
1031 {
1032 return new UrlEncoded(this);
1033 }
1034 }