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 quoteIfNeeded(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             for (int i=0;i<s.length();i++)
326             {
327                 char c = s.charAt(i);
328                 switch(c)
329                 {
330                     case '"':
331                         buf.append("\\\"");
332                         continue;
333                     case '\\':
334                         buf.append("\\\\");
335                         continue;
336                     case '\n':
337                         buf.append("\\n");
338                         continue;
339                     case '\r':
340                         buf.append("\\r");
341                         continue;
342                     case '\t':
343                         buf.append("\\t");
344                         continue;
345                     case '\f':
346                         buf.append("\\f");
347                         continue;
348                     case '\b':
349                         buf.append("\\b");
350                         continue;
351 
352                     default:
353                         if (c<0x10)
354                         {
355                             buf.append("\\u000");
356                             buf.append(Integer.toString(c,16));
357                         }
358                         else if (c<=0x1f)
359                         {
360                             buf.append("\\u00");
361                             buf.append(Integer.toString(c,16));
362                         }
363                         else
364                             buf.append(c);
365                         continue;
366                 }
367             }
368 
369             buf.append('"');
370         } 
371         catch(IOException e)
372         {
373             throw new RuntimeException(e);
374         }
375     }
376     
377     /* ------------------------------------------------------------ */
378     /** Quote a string into a StringBuffer only if needed.
379      * Quotes are forced if any delim characters are present.
380      * 
381      * @param buf The StringBuffer
382      * @param s The String to quote.
383      * @param delim String of characters that must be quoted.
384      * @return true if quoted;
385      */
386     public static boolean quoteIfNeeded(Appendable buf, String s,String delim)
387     {
388         for (int i=0;i<s.length();i++)
389         {
390             char c = s.charAt(i);
391             if (delim.indexOf(c)>=0)
392             {
393             	quote(buf,s);
394             	return true;
395             }
396         }
397     	
398         try
399         {
400             buf.append(s);
401             return false;
402         }
403         catch(IOException e)
404         {
405             throw new RuntimeException(e);
406         }
407     }
408     
409     /* ------------------------------------------------------------ */
410     /** Unquote a string.
411      * @param s The string to unquote.
412      * @return quoted string
413      */
414     public static String unquote(String s)
415     {
416         if (s==null)
417             return null;
418         if (s.length()<2)
419             return s;
420 
421         char first=s.charAt(0);
422         char last=s.charAt(s.length()-1);
423         if (first!=last || (first!='"' && first!='\''))
424             return s;
425         
426         StringBuffer b=new StringBuffer(s.length()-2);
427         synchronized(b)
428         {
429             boolean escape=false;
430             for (int i=1;i<s.length()-1;i++)
431             {
432                 char c = s.charAt(i);
433 
434                 if (escape)
435                 {
436                     escape=false;
437                     switch (c)
438                     {
439                         case 'n':
440                             b.append('\n');
441                             break;
442                         case 'r':
443                             b.append('\r');
444                             break;
445                         case 't':
446                             b.append('\t');
447                             break;
448                         case 'f':
449                             b.append('\f');
450                             break;
451                         case 'b':
452                             b.append('\b');
453                             break;
454                         case '\\':
455                             b.append('\\');
456                             break;
457                         case '/':
458                             b.append('/');
459                             break;
460                         case '"':
461                             b.append('"');
462                             break;
463                         case 'u':
464                             b.append((char)(
465                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
466                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
467                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
468                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
469                                     ) 
470                             );
471                             break;
472                         default:
473                             b.append(c);
474                     }
475                 }
476                 else if (c=='\\')
477                 {
478                     escape=true;
479                     continue;
480                 }
481                 else
482                     b.append(c);
483             }
484             
485             return b.toString();
486         }
487     }
488 
489     /* ------------------------------------------------------------ */
490     /**
491      * @return handle double quotes if true
492      */
493     public boolean getDouble()
494     {
495         return _double;
496     }
497 
498     /* ------------------------------------------------------------ */
499     /**
500      * @param d handle double quotes if true
501      */
502     public void setDouble(boolean d)
503     {
504         _double=d;
505     }
506 
507     /* ------------------------------------------------------------ */
508     /**
509      * @return handle single quotes if true
510      */
511     public boolean getSingle()
512     {
513         return _single;
514     }
515 
516     /* ------------------------------------------------------------ */
517     /**
518      * @param single handle single quotes if true
519      */
520     public void setSingle(boolean single)
521     {
522         _single=single;
523     }
524 }
525 
526 
527 
528 
529 
530 
531 
532 
533 
534 
535 
536