View Javadoc

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