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.io.IOException;
17  import java.util.NoSuchElementException;
18  import java.util.StringTokenizer;
19  
20  /* ------------------------------------------------------------ */
21  /** StringTokenizer with Quoting support.
22   *
23   * This class is a copy of the java.util.StringTokenizer API and
24   * the behaviour is the same, except that single and doulbe quoted
25   * string values are recognized.
26   * Delimiters within quotes are not considered delimiters.
27   * Quotes can be escaped with '\'.
28   *
29   * @see java.util.StringTokenizer
30   * 
31   */
32  public class QuotedStringTokenizer
33      extends StringTokenizer
34  {
35      private final static String __delim="\t\n\r";
36      private String _string;
37      private String _delim = __delim;
38      private boolean _returnQuotes=false;
39      private boolean _returnDelimiters=false;
40      private StringBuffer _token;
41      private boolean _hasToken=false;
42      private int _i=0;
43      private int _lastStart=0;
44      private boolean _double=true;
45      private boolean _single=true;
46      
47      /* ------------------------------------------------------------ */
48      public QuotedStringTokenizer(String str,
49                                   String delim,
50                                   boolean returnDelimiters,
51                                   boolean returnQuotes)
52      {
53          super("");
54          _string=str;
55          if (delim!=null)
56              _delim=delim;
57          _returnDelimiters=returnDelimiters;
58          _returnQuotes=returnQuotes;
59          
60          if (_delim.indexOf('\'')>=0 ||
61              _delim.indexOf('"')>=0)
62              throw new Error("Can't use quotes as delimiters: "+_delim);
63          
64          _token=new StringBuffer(_string.length()>1024?512:_string.length()/2);
65      }
66  
67      /* ------------------------------------------------------------ */
68      public QuotedStringTokenizer(String str,
69                                   String delim,
70                                   boolean returnDelimiters)
71      {
72          this(str,delim,returnDelimiters,false);
73      }
74      
75      /* ------------------------------------------------------------ */
76      public QuotedStringTokenizer(String str,
77                                   String delim)
78      {
79          this(str,delim,false,false);
80      }
81  
82      /* ------------------------------------------------------------ */
83      public QuotedStringTokenizer(String str)
84      {
85          this(str,null,false,false);
86      }
87  
88      /* ------------------------------------------------------------ */
89      @Override
90      public boolean hasMoreTokens()
91      {
92          // Already found a token
93          if (_hasToken)
94              return true;
95          
96          _lastStart=_i;
97          
98          int state=0;
99          boolean escape=false;
100         while (_i<_string.length())
101         {
102             char c=_string.charAt(_i++);
103             
104             switch (state)
105             {
106               case 0: // Start
107                   if(_delim.indexOf(c)>=0)
108                   {
109                       if (_returnDelimiters)
110                       {
111                           _token.append(c);
112                           return _hasToken=true;
113                       }
114                   }
115                   else if (c=='\'' && _single)
116                   {
117                       if (_returnQuotes)
118                           _token.append(c);
119                       state=2;
120                   }
121                   else if (c=='\"' && _double)
122                   {
123                       if (_returnQuotes)
124                           _token.append(c);
125                       state=3;
126                   }
127                   else
128                   {
129                       _token.append(c);
130                       _hasToken=true;
131                       state=1;
132                   }
133                   continue;
134                   
135               case 1: // Token
136                   _hasToken=true;
137                   if(_delim.indexOf(c)>=0)
138                   {
139                       if (_returnDelimiters)
140                           _i--;
141                       return _hasToken;
142                   }
143                   else if (c=='\'' && _single)
144                   {
145                       if (_returnQuotes)
146                           _token.append(c);
147                       state=2;
148                   }
149                   else if (c=='\"' && _double)
150                   {
151                       if (_returnQuotes)
152                           _token.append(c);
153                       state=3;
154                   }
155                   else
156                       _token.append(c);
157                   continue;
158 
159                   
160               case 2: // Single Quote
161                   _hasToken=true;
162                   if (escape)
163                   {
164                       escape=false;
165                       _token.append(c);
166                   }
167                   else if (c=='\'')
168                   {
169                       if (_returnQuotes)
170                           _token.append(c);
171                       state=1;
172                   }
173                   else if (c=='\\')
174                   {
175                       if (_returnQuotes)
176                           _token.append(c);
177                       escape=true;
178                   }
179                   else
180                       _token.append(c);
181                   continue;
182 
183                   
184               case 3: // Double Quote
185                   _hasToken=true;
186                   if (escape)
187                   {
188                       escape=false;
189                       _token.append(c);
190                   }
191                   else if (c=='\"')
192                   {
193                       if (_returnQuotes)
194                           _token.append(c);
195                       state=1;
196                   }
197                   else if (c=='\\')
198                   {
199                       if (_returnQuotes)
200                           _token.append(c);
201                       escape=true;
202                   }
203                   else
204                       _token.append(c);
205                   continue;
206             }
207         }
208 
209         return _hasToken;
210     }
211 
212     /* ------------------------------------------------------------ */
213     @Override
214     public String nextToken()
215         throws NoSuchElementException 
216     {
217         if (!hasMoreTokens() || _token==null)
218             throw new NoSuchElementException();
219         String t=_token.toString();
220         _token.setLength(0);
221         _hasToken=false;
222         return t;
223     }
224 
225     /* ------------------------------------------------------------ */
226     @Override
227     public String nextToken(String delim)
228         throws NoSuchElementException 
229     {
230         _delim=delim;
231         _i=_lastStart;
232         _token.setLength(0);
233         _hasToken=false;
234         return nextToken();
235     }
236 
237     /* ------------------------------------------------------------ */
238     @Override
239     public boolean hasMoreElements()
240     {
241         return hasMoreTokens();
242     }
243 
244     /* ------------------------------------------------------------ */
245     @Override
246     public Object nextElement()
247         throws NoSuchElementException 
248     {
249         return nextToken();
250     }
251 
252     /* ------------------------------------------------------------ */
253     /** Not implemented.
254      */
255     @Override
256     public int countTokens()
257     {
258         return -1;
259     }
260 
261     
262     /* ------------------------------------------------------------ */
263     /** Quote a string.
264      * The string is quoted only if quoting is required due to
265      * embeded delimiters, quote characters or the
266      * empty string.
267      * @param s The string to quote.
268      * @return quoted string
269      */
270     public static String quote(String s, String delim)
271     {
272         if (s==null)
273             return null;
274         if (s.length()==0)
275             return "\"\"";
276 
277         
278         for (int i=0;i<s.length();i++)
279         {
280             char c = s.charAt(i);
281             if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0)
282             {
283                 StringBuffer b=new StringBuffer(s.length()+8);
284                 quote(b,s);
285                 return b.toString();
286             }
287         }
288         
289         return s;
290     }
291 
292     /* ------------------------------------------------------------ */
293     /** Quote a string.
294      * The string is quoted only if quoting is required due to
295      * embeded delimiters, quote characters or the
296      * empty string.
297      * @param s The string to quote.
298      * @return quoted string
299      */
300     public static String quote(String s)
301     {
302         if (s==null)
303             return null;
304         if (s.length()==0)
305             return "\"\"";
306         
307         StringBuffer b=new StringBuffer(s.length()+8);
308         quote(b,s);
309         return b.toString();
310    
311     }
312 
313     /* ------------------------------------------------------------ */
314     /** Quote a string into an Appendable.
315      * The characters ", \, \n, \r, \t, \f and \b are escaped
316      * @param buf The Appendable
317      * @param s The String to quote.
318      */
319     public static void quote(Appendable buf, String s)
320     {
321         try
322         {
323             buf.append('"');
324 
325             int i=0;
326             loop:
327                 for (;i<s.length();i++)
328                 {
329                     char c = s.charAt(i);
330                     switch(c)
331                     {
332                         case '"':
333                             buf.append(s,0,i);
334                             buf.append("\\\"");
335                             break loop;
336                         case '\\':
337                             buf.append(s,0,i);
338                             buf.append("\\\\");
339                             break loop;
340                         case '\n':
341                             buf.append(s,0,i);
342                             buf.append("\\n");
343                             break loop;
344                         case '\r':
345                             buf.append(s,0,i);
346                             buf.append("\\r");
347                             break loop;
348                         case '\t':
349                             buf.append(s,0,i);
350                             buf.append("\\t");
351                             break loop;
352                         case '\f':
353                             buf.append(s,0,i);
354                             buf.append("\\f");
355                             break loop;
356                         case '\b':
357                             buf.append(s,0,i);
358                             buf.append("\\b");
359                             break loop;
360 
361                         default:
362                             continue;
363                     }
364                 }
365             if (i==s.length())
366                 buf.append(s);
367             else
368             {
369                 i++;
370                 for (;i<s.length();i++)
371                 {
372                     char c = s.charAt(i);
373                     switch(c)
374                     {
375                         case '"':
376                             buf.append("\\\"");
377                             continue;
378                         case '\\':
379                             buf.append("\\\\");
380                             continue;
381                         case '\n':
382                             buf.append("\\n");
383                             continue;
384                         case '\r':
385                             buf.append("\\r");
386                             continue;
387                         case '\t':
388                             buf.append("\\t");
389                             continue;
390                         case '\f':
391                             buf.append("\\f");
392                             continue;
393                         case '\b':
394                             buf.append("\\b");
395                             continue;
396 
397                         default:
398                             buf.append(c);
399                             continue;
400                     }
401                 }
402             }
403 
404             buf.append('"');
405         } 
406         catch(IOException e)
407         {
408             throw new RuntimeException(e);
409         }
410     }
411 
412 
413 
414 
415     /* ------------------------------------------------------------ */
416     /** Quote a string into a StringBuffer.
417      * The characters ", \, \n, \r, \t, \f, \b are escaped.
418      * Quotes are forced if any escaped characters are present or there
419      * is a ", ', space, +, =, ; or % character.
420      * 
421      * @param buf The StringBuffer
422      * @param s The String to quote.
423      */
424     public static void quoteIfNeeded(Appendable buf, String s)
425     {
426         try
427         {
428             int e=-1;
429 
430             search: for (int i=0;i<s.length();i++)
431             {
432                 char c = s.charAt(i);
433                 switch(c)
434                 {
435                     case '"':
436                     case '\\':
437                     case '\n':
438                     case '\r':
439                     case '\t':
440                     case '\f':
441                     case '\b':
442                     case '%':
443                     case '+':
444                     case ' ':
445                     case ';':
446                     case '=':
447                         e=i;
448                         buf.append('"');
449                         // TODO when 1.4 support is dropped: buf.append(s,0,e);
450                         for (int j=0;j<e;j++)
451                             buf.append(s.charAt(j));
452                         break search;
453 
454                     default:
455                         continue;
456                 }
457             }
458 
459             if (e<0)
460             {
461                 buf.append(s);
462                 return;
463             }
464 
465             for (int i=e;i<s.length();i++)
466             {
467                 char c = s.charAt(i);
468                 switch(c)
469                 {
470                     case '"':
471                         buf.append("\\\"");
472                         continue;
473                     case '\\':
474                         buf.append("\\\\");
475                         continue;
476                     case '\n':
477                         buf.append("\\n");
478                         continue;
479                     case '\r':
480                         buf.append("\\r");
481                         continue;
482                     case '\t':
483                         buf.append("\\t");
484                         continue;
485                     case '\f':
486                         buf.append("\\f");
487                         continue;
488                     case '\b':
489                         buf.append("\\b");
490                         continue;
491 
492                     default:
493                         buf.append(c);
494                         continue;
495                 }
496             }
497             buf.append('"');
498         }
499         catch(IOException e)
500         {
501             throw new RuntimeException(e);
502         }
503     }
504     
505     /* ------------------------------------------------------------ */
506     /** Unquote a string.
507      * @param s The string to unquote.
508      * @return quoted string
509      */
510     public static String unquote(String s)
511     {
512         if (s==null)
513             return null;
514         if (s.length()<2)
515             return s;
516 
517         char first=s.charAt(0);
518         char last=s.charAt(s.length()-1);
519         if (first!=last || (first!='"' && first!='\''))
520             return s;
521         
522         StringBuffer b=new StringBuffer(s.length()-2);
523         synchronized(b)
524         {
525             boolean escape=false;
526             for (int i=1;i<s.length()-1;i++)
527             {
528                 char c = s.charAt(i);
529 
530                 if (escape)
531                 {
532                     escape=false;
533                     switch (c)
534                     {
535                         case 'n':
536                             b.append('\n');
537                             break;
538                         case 'r':
539                             b.append('\r');
540                             break;
541                         case 't':
542                             b.append('\t');
543                             break;
544                         case 'f':
545                             b.append('\f');
546                             break;
547                         case 'b':
548                             b.append('\b');
549                             break;
550                         case 'u':
551                             b.append((char)(
552                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
553                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
554                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
555                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
556                                     ) 
557                             );
558                             break;
559                         default:
560                             b.append(c);
561                     }
562                 }
563                 else if (c=='\\')
564                 {
565                     escape=true;
566                     continue;
567                 }
568                 else
569                     b.append(c);
570             }
571             
572             return b.toString();
573         }
574     }
575 
576     /* ------------------------------------------------------------ */
577     /**
578      * @return handle double quotes if true
579      */
580     public boolean getDouble()
581     {
582         return _double;
583     }
584 
585     /* ------------------------------------------------------------ */
586     /**
587      * @param d handle double quotes if true
588      */
589     public void setDouble(boolean d)
590     {
591         _double=d;
592     }
593 
594     /* ------------------------------------------------------------ */
595     /**
596      * @return handle single quotes if true
597      */
598     public boolean getSingle()
599     {
600         return _single;
601     }
602 
603     /* ------------------------------------------------------------ */
604     /**
605      * @param single handle single quotes if true
606      */
607     public void setSingle(boolean single)
608     {
609         _single=single;
610     }
611 }
612 
613 
614 
615 
616 
617 
618 
619 
620 
621 
622 
623