Mercurial Hosting > luan
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 } |