1 // ========================================================================
2 // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.util;
15
16 import java.util.NoSuchElementException;
17 import java.util.StringTokenizer;
18
19 /* ------------------------------------------------------------ */
20 /** StringTokenizer with Quoting support.
21 *
22 * This class is a copy of the java.util.StringTokenizer API and
23 * the behaviour is the same, except that single and doulbe quoted
24 * string values are recognized.
25 * Delimiters within quotes are not considered delimiters.
26 * Quotes can be escaped with '\'.
27 *
28 * @see java.util.StringTokenizer
29 *
30 */
31 public class QuotedStringTokenizer
32 extends StringTokenizer
33 {
34 private final static String __delim="\t\n\r";
35 private String _string;
36 private String _delim = __delim;
37 private boolean _returnQuotes=false;
38 private boolean _returnDelimiters=false;
39 private StringBuffer _token;
40 private boolean _hasToken=false;
41 private int _i=0;
42 private int _lastStart=0;
43 private boolean _double=true;
44 private boolean _single=true;
45
46 /* ------------------------------------------------------------ */
47 public QuotedStringTokenizer(String str,
48 String delim,
49 boolean returnDelimiters,
50 boolean returnQuotes)
51 {
52 super("");
53 _string=str;
54 if (delim!=null)
55 _delim=delim;
56 _returnDelimiters=returnDelimiters;
57 _returnQuotes=returnQuotes;
58
59 if (_delim.indexOf('\'')>=0 ||
60 _delim.indexOf('"')>=0)
61 throw new Error("Can't use quotes as delimiters: "+_delim);
62
63 _token=new StringBuffer(_string.length()>1024?512:_string.length()/2);
64 }
65
66 /* ------------------------------------------------------------ */
67 public QuotedStringTokenizer(String str,
68 String delim,
69 boolean returnDelimiters)
70 {
71 this(str,delim,returnDelimiters,false);
72 }
73
74 /* ------------------------------------------------------------ */
75 public QuotedStringTokenizer(String str,
76 String delim)
77 {
78 this(str,delim,false,false);
79 }
80
81 /* ------------------------------------------------------------ */
82 public QuotedStringTokenizer(String str)
83 {
84 this(str,null,false,false);
85 }
86
87 /* ------------------------------------------------------------ */
88 public boolean hasMoreTokens()
89 {
90 // Already found a token
91 if (_hasToken)
92 return true;
93
94 _lastStart=_i;
95
96 int state=0;
97 boolean escape=false;
98 while (_i<_string.length())
99 {
100 char c=_string.charAt(_i++);
101
102 switch (state)
103 {
104 case 0: // Start
105 if(_delim.indexOf(c)>=0)
106 {
107 if (_returnDelimiters)
108 {
109 _token.append(c);
110 return _hasToken=true;
111 }
112 }
113 else if (c=='\'' && _single)
114 {
115 if (_returnQuotes)
116 _token.append(c);
117 state=2;
118 }
119 else if (c=='\"' && _double)
120 {
121 if (_returnQuotes)
122 _token.append(c);
123 state=3;
124 }
125 else
126 {
127 _token.append(c);
128 _hasToken=true;
129 state=1;
130 }
131 continue;
132
133 case 1: // Token
134 _hasToken=true;
135 if(_delim.indexOf(c)>=0)
136 {
137 if (_returnDelimiters)
138 _i--;
139 return _hasToken;
140 }
141 else if (c=='\'' && _single)
142 {
143 if (_returnQuotes)
144 _token.append(c);
145 state=2;
146 }
147 else if (c=='\"' && _double)
148 {
149 if (_returnQuotes)
150 _token.append(c);
151 state=3;
152 }
153 else
154 _token.append(c);
155 continue;
156
157
158 case 2: // Single Quote
159 _hasToken=true;
160 if (escape)
161 {
162 escape=false;
163 _token.append(c);
164 }
165 else if (c=='\'')
166 {
167 if (_returnQuotes)
168 _token.append(c);
169 state=1;
170 }
171 else if (c=='\\')
172 {
173 if (_returnQuotes)
174 _token.append(c);
175 escape=true;
176 }
177 else
178 _token.append(c);
179 continue;
180
181
182 case 3: // Double Quote
183 _hasToken=true;
184 if (escape)
185 {
186 escape=false;
187 _token.append(c);
188 }
189 else if (c=='\"')
190 {
191 if (_returnQuotes)
192 _token.append(c);
193 state=1;
194 }
195 else if (c=='\\')
196 {
197 if (_returnQuotes)
198 _token.append(c);
199 escape=true;
200 }
201 else
202 _token.append(c);
203 continue;
204 }
205 }
206
207 return _hasToken;
208 }
209
210 /* ------------------------------------------------------------ */
211 public String nextToken()
212 throws NoSuchElementException
213 {
214 if (!hasMoreTokens() || _token==null)
215 throw new NoSuchElementException();
216 String t=_token.toString();
217 _token.setLength(0);
218 _hasToken=false;
219 return t;
220 }
221
222 /* ------------------------------------------------------------ */
223 public String nextToken(String delim)
224 throws NoSuchElementException
225 {
226 _delim=delim;
227 _i=_lastStart;
228 _token.setLength(0);
229 _hasToken=false;
230 return nextToken();
231 }
232
233 /* ------------------------------------------------------------ */
234 public boolean hasMoreElements()
235 {
236 return hasMoreTokens();
237 }
238
239 /* ------------------------------------------------------------ */
240 public Object nextElement()
241 throws NoSuchElementException
242 {
243 return nextToken();
244 }
245
246 /* ------------------------------------------------------------ */
247 /** Not implemented.
248 */
249 public int countTokens()
250 {
251 return -1;
252 }
253
254
255 /* ------------------------------------------------------------ */
256 /** Quote a string.
257 * The string is quoted only if quoting is required due to
258 * embeded delimiters, quote characters or the
259 * empty string.
260 * @param s The string to quote.
261 * @return quoted string
262 */
263 public static String quote(String s, String delim)
264 {
265 if (s==null)
266 return null;
267 if (s.length()==0)
268 return "\"\"";
269
270
271 for (int i=0;i<s.length();i++)
272 {
273 char c = s.charAt(i);
274 if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0)
275 {
276 StringBuffer b=new StringBuffer(s.length()+8);
277 quote(b,s);
278 return b.toString();
279 }
280 }
281
282 return s;
283 }
284
285 /* ------------------------------------------------------------ */
286 /** Quote a string.
287 * The string is quoted only if quoting is required due to
288 * embeded delimiters, quote characters or the
289 * empty string.
290 * @param s The string to quote.
291 * @return quoted string
292 */
293 public static String quote(String s)
294 {
295 if (s==null)
296 return null;
297 if (s.length()==0)
298 return "\"\"";
299
300 StringBuffer b=new StringBuffer(s.length()+8);
301 quote(b,s);
302 return b.toString();
303
304 }
305
306
307 /* ------------------------------------------------------------ */
308 /** Quote a string into a StringBuffer.
309 * The characters ", \, \n, \r, \t, \f and \b are escaped
310 * @param buf The StringBuffer
311 * @param s The String to quote.
312 */
313 public static void quote(StringBuffer buf, String s)
314 {
315 synchronized(buf)
316 {
317 buf.append('"');
318
319 int i=0;
320 loop:
321 for (;i<s.length();i++)
322 {
323 char c = s.charAt(i);
324 switch(c)
325 {
326 case '"':
327 buf.append(s,0,i);
328 buf.append("\\\"");
329 break loop;
330 case '\\':
331 buf.append(s,0,i);
332 buf.append("\\\\");
333 break loop;
334 case '\n':
335 buf.append(s,0,i);
336 buf.append("\\n");
337 break loop;
338 case '\r':
339 buf.append(s,0,i);
340 buf.append("\\r");
341 break loop;
342 case '\t':
343 buf.append(s,0,i);
344 buf.append("\\t");
345 break loop;
346 case '\f':
347 buf.append(s,0,i);
348 buf.append("\\f");
349 break loop;
350 case '\b':
351 buf.append(s,0,i);
352 buf.append("\\b");
353 break loop;
354
355 default:
356 continue;
357 }
358 }
359 if (i==s.length())
360 buf.append(s);
361 else
362 {
363 i++;
364 for (;i<s.length();i++)
365 {
366 char c = s.charAt(i);
367 switch(c)
368 {
369 case '"':
370 buf.append("\\\"");
371 continue;
372 case '\\':
373 buf.append("\\\\");
374 continue;
375 case '\n':
376 buf.append("\\n");
377 continue;
378 case '\r':
379 buf.append("\\r");
380 continue;
381 case '\t':
382 buf.append("\\t");
383 continue;
384 case '\f':
385 buf.append("\\f");
386 continue;
387 case '\b':
388 buf.append("\\b");
389 continue;
390
391 default:
392 buf.append(c);
393 continue;
394 }
395 }
396 }
397
398 buf.append('"');
399 }
400
401
402
403 }
404
405
406 /* ------------------------------------------------------------ */
407 /** Quote a string into a StringBuffer.
408 * The characters ", \, \n, \r, \t, \f and \b are escaped
409 * @param buf The StringBuffer
410 * @param s The String to quote.
411 */
412 public static void quote(StringBuilder buf, String s)
413 {
414 buf.append('"');
415
416 int i=0;
417 loop:
418 for (;i<s.length();i++)
419 {
420 char c = s.charAt(i);
421 switch(c)
422 {
423 case '"':
424 buf.append(s,0,i);
425 buf.append("\\\"");
426 break loop;
427 case '\\':
428 buf.append(s,0,i);
429 buf.append("\\\\");
430 break loop;
431 case '\n':
432 buf.append(s,0,i);
433 buf.append("\\n");
434 break loop;
435 case '\r':
436 buf.append(s,0,i);
437 buf.append("\\r");
438 break loop;
439 case '\t':
440 buf.append(s,0,i);
441 buf.append("\\t");
442 break loop;
443 case '\f':
444 buf.append(s,0,i);
445 buf.append("\\f");
446 break loop;
447 case '\b':
448 buf.append(s,0,i);
449 buf.append("\\b");
450 break loop;
451
452 default:
453 continue;
454 }
455 }
456 if (i==s.length())
457 buf.append(s);
458 else
459 {
460 i++;
461 for (;i<s.length();i++)
462 {
463 char c = s.charAt(i);
464 switch(c)
465 {
466 case '"':
467 buf.append("\\\"");
468 continue;
469 case '\\':
470 buf.append("\\\\");
471 continue;
472 case '\n':
473 buf.append("\\n");
474 continue;
475 case '\r':
476 buf.append("\\r");
477 continue;
478 case '\t':
479 buf.append("\\t");
480 continue;
481 case '\f':
482 buf.append("\\f");
483 continue;
484 case '\b':
485 buf.append("\\b");
486 continue;
487
488 default:
489 buf.append(c);
490 continue;
491 }
492 }
493 }
494
495 buf.append('"');
496 }
497
498
499
500
501
502 /* ------------------------------------------------------------ */
503 /** Quote a string into a StringBuffer.
504 * The characters ", \, \n, \r, \t, \f, \b are escaped.
505 * Quotes are forced if any escaped characters are present or there
506 * is a ", ', space, +, =, ; or % character.
507 *
508 * @param buf The StringBuffer
509 * @param s The String to quote.
510 */
511 public static void quoteIfNeeded(StringBuffer buf, String s)
512 {
513 synchronized(buf)
514 {
515 int e=-1;
516
517 search: for (int i=0;i<s.length();i++)
518 {
519 char c = s.charAt(i);
520 switch(c)
521 {
522 case '"':
523 case '\\':
524 case '\n':
525 case '\r':
526 case '\t':
527 case '\f':
528 case '\b':
529 case '%':
530 case '+':
531 case ' ':
532 case ';':
533 case '=':
534 e=i;
535 buf.append('"');
536 // TODO when 1.4 support is dropped: buf.append(s,0,e);
537 for (int j=0;j<e;j++)
538 buf.append(s.charAt(j));
539 break search;
540
541 default:
542 continue;
543 }
544 }
545
546 if (e<0)
547 {
548 buf.append(s);
549 return;
550 }
551
552 for (int i=e;i<s.length();i++)
553 {
554 char c = s.charAt(i);
555 switch(c)
556 {
557 case '"':
558 buf.append("\\\"");
559 continue;
560 case '\\':
561 buf.append("\\\\");
562 continue;
563 case '\n':
564 buf.append("\\n");
565 continue;
566 case '\r':
567 buf.append("\\r");
568 continue;
569 case '\t':
570 buf.append("\\t");
571 continue;
572 case '\f':
573 buf.append("\\f");
574 continue;
575 case '\b':
576 buf.append("\\b");
577 continue;
578
579 default:
580 buf.append(c);
581 continue;
582 }
583 }
584 buf.append('"');
585 }
586 }
587
588 /* ------------------------------------------------------------ */
589 /** Quote a string into a StringBuffer.
590 * The characters ", \, \n, \r, \t, \f, \b are escaped.
591 * Quotes are forced if any escaped characters are present or there
592 * is a ", ', space, + or % character.
593 *
594 * @param buf The StringBuilder
595 * @param s The String to quote.
596 */
597 public static void quoteIfNeeded(StringBuilder buf, String s)
598 {
599 int e=-1;
600
601 search: for (int i=0;i<s.length();i++)
602 {
603 char c = s.charAt(i);
604 switch(c)
605 {
606 case '"':
607 case '\\':
608 case '\n':
609 case '\r':
610 case '\t':
611 case '\f':
612 case '\b':
613 case '%':
614 case '+':
615 case ' ':
616 case ';':
617 case '=':
618 e=i;
619 buf.append('"');
620 // TODO when 1.4 support is dropped: buf.append(s,0,e);
621 for (int j=0;j<e;j++)
622 buf.append(s.charAt(j));
623 break search;
624
625 default:
626 continue;
627 }
628 }
629
630 if (e<0)
631 {
632 buf.append(s);
633 return;
634 }
635
636 for (int i=e;i<s.length();i++)
637 {
638 char c = s.charAt(i);
639 switch(c)
640 {
641 case '"':
642 buf.append("\\\"");
643 continue;
644 case '\\':
645 buf.append("\\\\");
646 continue;
647 case '\n':
648 buf.append("\\n");
649 continue;
650 case '\r':
651 buf.append("\\r");
652 continue;
653 case '\t':
654 buf.append("\\t");
655 continue;
656 case '\f':
657 buf.append("\\f");
658 continue;
659 case '\b':
660 buf.append("\\b");
661 continue;
662
663 default:
664 buf.append(c);
665 continue;
666 }
667 }
668 buf.append('"');
669
670 }
671
672 /* ------------------------------------------------------------ */
673 /** Unquote a string.
674 * @param s The string to unquote.
675 * @return quoted string
676 */
677 public static String unquote(String s)
678 {
679 if (s==null)
680 return null;
681 if (s.length()<2)
682 return s;
683
684 char first=s.charAt(0);
685 char last=s.charAt(s.length()-1);
686 if (first!=last || (first!='"' && first!='\''))
687 return s;
688
689 StringBuffer b=new StringBuffer(s.length()-2);
690 synchronized(b)
691 {
692 boolean escape=false;
693 for (int i=1;i<s.length()-1;i++)
694 {
695 char c = s.charAt(i);
696
697 if (escape)
698 {
699 escape=false;
700 switch (c)
701 {
702 case 'n':
703 b.append('\n');
704 break;
705 case 'r':
706 b.append('\r');
707 break;
708 case 't':
709 b.append('\t');
710 break;
711 case 'f':
712 b.append('\f');
713 break;
714 case 'b':
715 b.append('\b');
716 break;
717 case 'u':
718 b.append((char)(
719 (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
720 (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
721 (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
722 (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
723 )
724 );
725 break;
726 default:
727 b.append(c);
728 }
729 }
730 else if (c=='\\')
731 {
732 escape=true;
733 continue;
734 }
735 else
736 b.append(c);
737 }
738
739 return b.toString();
740 }
741 }
742
743 /* ------------------------------------------------------------ */
744 /**
745 * @return handle double quotes if true
746 */
747 public boolean getDouble()
748 {
749 return _double;
750 }
751
752 /* ------------------------------------------------------------ */
753 /**
754 * @param d handle double quotes if true
755 */
756 public void setDouble(boolean d)
757 {
758 _double=d;
759 }
760
761 /* ------------------------------------------------------------ */
762 /**
763 * @return handle single quotes if true
764 */
765 public boolean getSingle()
766 {
767 return _single;
768 }
769
770 /* ------------------------------------------------------------ */
771 /**
772 * @param single handle single quotes if true
773 */
774 public void setSingle(boolean single)
775 {
776 _single=single;
777 }
778 }
779
780
781
782
783
784
785
786
787
788
789
790