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                         e=i;
533                         buf.append('"');
534                         // TODO when 1.4 support is dropped: buf.append(s,0,e);
535                         for (int j=0;j<e;j++)
536                             buf.append(s.charAt(j));
537                         break search;
538                         
539                     default:
540                         continue;
541                 }
542             }
543             
544             if (e<0)
545             {
546                 buf.append(s);
547                 return;
548             }
549             
550             for (int i=e;i<s.length();i++)
551             {
552                 char c = s.charAt(i);
553                 switch(c)
554                 {
555                     case '"':
556                         buf.append("\\\"");
557                         continue;
558                     case '\\':
559                         buf.append("\\\\");
560                         continue;
561                     case '\n':
562                         buf.append("\\n");
563                         continue;
564                     case '\r':
565                         buf.append("\\r");
566                         continue;
567                     case '\t':
568                         buf.append("\\t");
569                         continue;
570                     case '\f':
571                         buf.append("\\f");
572                         continue;
573                     case '\b':
574                         buf.append("\\b");
575                         continue;
576                         
577                     default:
578                         buf.append(c);
579                         continue;
580                 }
581             }
582             buf.append('"');
583         }
584     }
585     
586     /* ------------------------------------------------------------ */
587     /** Quote a string into a StringBuffer.
588      * The characters ", \, \n, \r, \t, \f, \b are escaped.
589      * Quotes are forced if any escaped characters are present or there
590      * is a ", ', space, + or % character.
591      * 
592      * @param buf The StringBuilder
593      * @param s The String to quote.
594      */
595     public static void quoteIfNeeded(StringBuilder buf, String s)
596     {
597         int e=-1;
598 
599         search: for (int i=0;i<s.length();i++)
600         {
601             char c = s.charAt(i);
602             switch(c)
603             {
604                 case '"':
605                 case '\\':
606                 case '\n':
607                 case '\r':
608                 case '\t':
609                 case '\f':
610                 case '\b':
611                 case '%':
612                 case '+':
613                 case ' ':
614                     e=i;
615                     buf.append('"');
616                     // TODO when 1.4 support is dropped: buf.append(s,0,e);
617                     for (int j=0;j<e;j++)
618                         buf.append(s.charAt(j));
619                     break search;
620 
621                 default:
622                     continue;
623             }
624         }
625 
626         if (e<0)
627         {
628             buf.append(s);
629             return;
630         }
631 
632         for (int i=e;i<s.length();i++)
633         {
634             char c = s.charAt(i);
635             switch(c)
636             {
637                 case '"':
638                     buf.append("\\\"");
639                     continue;
640                 case '\\':
641                     buf.append("\\\\");
642                     continue;
643                 case '\n':
644                     buf.append("\\n");
645                     continue;
646                 case '\r':
647                     buf.append("\\r");
648                     continue;
649                 case '\t':
650                     buf.append("\\t");
651                     continue;
652                 case '\f':
653                     buf.append("\\f");
654                     continue;
655                 case '\b':
656                     buf.append("\\b");
657                     continue;
658 
659                 default:
660                     buf.append(c);
661                 continue;
662             }
663         }
664         buf.append('"');
665 
666     }
667     
668     /* ------------------------------------------------------------ */
669     /** Unquote a string.
670      * @param s The string to unquote.
671      * @return quoted string
672      */
673     public static String unquote(String s)
674     {
675         if (s==null)
676             return null;
677         if (s.length()<2)
678             return s;
679 
680         char first=s.charAt(0);
681         char last=s.charAt(s.length()-1);
682         if (first!=last || (first!='"' && first!='\''))
683             return s;
684         
685         StringBuffer b=new StringBuffer(s.length()-2);
686         synchronized(b)
687         {
688             boolean escape=false;
689             for (int i=1;i<s.length()-1;i++)
690             {
691                 char c = s.charAt(i);
692 
693                 if (escape)
694                 {
695                     escape=false;
696                     switch (c)
697                     {
698                         case 'n':
699                             b.append('\n');
700                             break;
701                         case 'r':
702                             b.append('\r');
703                             break;
704                         case 't':
705                             b.append('\t');
706                             break;
707                         case 'f':
708                             b.append('\f');
709                             break;
710                         case 'b':
711                             b.append('\b');
712                             break;
713                         case 'u':
714                             b.append((char)(
715                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
716                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
717                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
718                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
719                                     ) 
720                             );
721                             break;
722                         default:
723                             b.append(c);
724                     }
725                 }
726                 else if (c=='\\')
727                 {
728                     escape=true;
729                     continue;
730                 }
731                 else
732                     b.append(c);
733             }
734             
735             return b.toString();
736         }
737     }
738 
739     /* ------------------------------------------------------------ */
740     /**
741      * @return handle double quotes if true
742      */
743     public boolean getDouble()
744     {
745         return _double;
746     }
747 
748     /* ------------------------------------------------------------ */
749     /**
750      * @param d handle double quotes if true
751      */
752     public void setDouble(boolean d)
753     {
754         _double=d;
755     }
756 
757     /* ------------------------------------------------------------ */
758     /**
759      * @return handle single quotes if true
760      */
761     public boolean getSingle()
762     {
763         return _single;
764     }
765 
766     /* ------------------------------------------------------------ */
767     /**
768      * @param single handle single quotes if true
769      */
770     public void setSingle(boolean single)
771     {
772         _single=single;
773     }
774 }
775 
776 
777 
778 
779 
780 
781 
782 
783 
784 
785 
786