View Javadoc

1   // ========================================================================
2   // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.util;
16  
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   * @author Greg Wilkins (gregw)
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      public boolean hasMoreTokens()
90      {
91          // Already found a token
92          if (_hasToken)
93              return true;
94          
95          _lastStart=_i;
96          
97          int state=0;
98          boolean escape=false;
99          while (_i<_string.length())
100         {
101             char c=_string.charAt(_i++);
102             
103             switch (state)
104             {
105               case 0: // Start
106                   if(_delim.indexOf(c)>=0)
107                   {
108                       if (_returnDelimiters)
109                       {
110                           _token.append(c);
111                           return _hasToken=true;
112                       }
113                   }
114                   else if (c=='\'' && _single)
115                   {
116                       if (_returnQuotes)
117                           _token.append(c);
118                       state=2;
119                   }
120                   else if (c=='\"' && _double)
121                   {
122                       if (_returnQuotes)
123                           _token.append(c);
124                       state=3;
125                   }
126                   else
127                   {
128                       _token.append(c);
129                       _hasToken=true;
130                       state=1;
131                   }
132                   continue;
133                   
134               case 1: // Token
135                   _hasToken=true;
136                   if(_delim.indexOf(c)>=0)
137                   {
138                       if (_returnDelimiters)
139                           _i--;
140                       return _hasToken;
141                   }
142                   else if (c=='\'' && _single)
143                   {
144                       if (_returnQuotes)
145                           _token.append(c);
146                       state=2;
147                   }
148                   else if (c=='\"' && _double)
149                   {
150                       if (_returnQuotes)
151                           _token.append(c);
152                       state=3;
153                   }
154                   else
155                       _token.append(c);
156                   continue;
157 
158                   
159               case 2: // Single Quote
160                   _hasToken=true;
161                   if (escape)
162                   {
163                       escape=false;
164                       _token.append(c);
165                   }
166                   else if (c=='\'')
167                   {
168                       if (_returnQuotes)
169                           _token.append(c);
170                       state=1;
171                   }
172                   else if (c=='\\')
173                   {
174                       if (_returnQuotes)
175                           _token.append(c);
176                       escape=true;
177                   }
178                   else
179                       _token.append(c);
180                   continue;
181 
182                   
183               case 3: // Double Quote
184                   _hasToken=true;
185                   if (escape)
186                   {
187                       escape=false;
188                       _token.append(c);
189                   }
190                   else if (c=='\"')
191                   {
192                       if (_returnQuotes)
193                           _token.append(c);
194                       state=1;
195                   }
196                   else if (c=='\\')
197                   {
198                       if (_returnQuotes)
199                           _token.append(c);
200                       escape=true;
201                   }
202                   else
203                       _token.append(c);
204                   continue;
205             }
206         }
207 
208         return _hasToken;
209     }
210 
211     /* ------------------------------------------------------------ */
212     public String nextToken()
213         throws NoSuchElementException 
214     {
215         if (!hasMoreTokens() || _token==null)
216             throw new NoSuchElementException();
217         String t=_token.toString();
218         _token.setLength(0);
219         _hasToken=false;
220         return t;
221     }
222 
223     /* ------------------------------------------------------------ */
224     public String nextToken(String delim)
225         throws NoSuchElementException 
226     {
227         _delim=delim;
228         _i=_lastStart;
229         _token.setLength(0);
230         _hasToken=false;
231         return nextToken();
232     }
233 
234     /* ------------------------------------------------------------ */
235     public boolean hasMoreElements()
236     {
237         return hasMoreTokens();
238     }
239 
240     /* ------------------------------------------------------------ */
241     public Object nextElement()
242         throws NoSuchElementException 
243     {
244         return nextToken();
245     }
246 
247     /* ------------------------------------------------------------ */
248     /** Not implemented.
249      */
250     public int countTokens()
251     {
252         return -1;
253     }
254 
255     
256     /* ------------------------------------------------------------ */
257     /** Quote a string.
258      * The string is quoted only if quoting is required due to
259      * embeded delimiters, quote characters or the
260      * empty string.
261      * @param s The string to quote.
262      * @return quoted string
263      */
264     public static String quote(String s, String delim)
265     {
266         if (s==null)
267             return null;
268         if (s.length()==0)
269             return "\"\"";
270 
271         
272         for (int i=0;i<s.length();i++)
273         {
274             char c = s.charAt(i);
275             if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0)
276             {
277                 StringBuffer b=new StringBuffer(s.length()+8);
278                 quote(b,s);
279                 return b.toString();
280             }
281         }
282         
283         return s;
284     }
285 
286     /* ------------------------------------------------------------ */
287     /** Quote a string.
288      * The string is quoted only if quoting is required due to
289      * embeded delimiters, quote characters or the
290      * empty string.
291      * @param s The string to quote.
292      * @return quoted string
293      */
294     public static String quote(String s)
295     {
296         if (s==null)
297             return null;
298         if (s.length()==0)
299             return "\"\"";
300         
301         StringBuffer b=new StringBuffer(s.length()+8);
302         quote(b,s);
303         return b.toString();
304    
305     }
306 
307     
308     /* ------------------------------------------------------------ */
309     /** Quote a string into a StringBuffer.
310      * The characters ", \, \n, \r, \t, \f and \b are escaped
311      * @param buf The StringBuffer
312      * @param s The String to quote.
313      */
314     public static void quote(StringBuffer buf, String s)
315     {
316         synchronized(buf)
317         {
318             buf.append('"');
319             char[] chars = null;
320             int i=0;
321             loop:
322             for (;i<s.length();i++)
323             {
324                 char c = s.charAt(i);
325                 switch(c)
326                 {
327                     case '"':
328                         chars = s.toCharArray();
329                         buf.append(chars,0,i);
330                         buf.append("\\\"");
331                         break loop;
332                     case '\\':
333                         chars = s.toCharArray();
334                         buf.append(chars,0,i);
335                         buf.append("\\\\");
336                         break loop;
337                     case '\n':
338                         chars = s.toCharArray();
339                         buf.append(chars,0,i);
340                         buf.append("\\n");
341                         break loop;
342                     case '\r':
343                         chars = s.toCharArray();
344                         buf.append(chars,0,i);
345                         buf.append("\\r");
346                         break loop;
347                     case '\t':
348                         chars = s.toCharArray();
349                         buf.append(chars,0,i);
350                         buf.append("\\t");
351                         break loop;
352                     case '\f':
353                         chars = s.toCharArray();
354                         buf.append(chars,0,i);
355                         buf.append("\\f");
356                         break loop;
357                     case '\b':
358                         chars = s.toCharArray();
359                         buf.append(chars,0,i);
360                         buf.append("\\b");
361                         break loop;
362                         
363                     default:
364                         continue;
365                 }
366             }
367             if (chars==null)
368                 buf.append(s);
369             else
370             {
371                 i++;
372                 for (;i<s.length();i++)
373                 {
374                     char c = s.charAt(i);
375                     switch(c)
376                     {
377                         case '"':
378                             buf.append("\\\"");
379                             continue;
380                         case '\\':
381                             buf.append("\\\\");
382                             continue;
383                         case '\n':
384                             buf.append("\\n");
385                             continue;
386                         case '\r':
387                             buf.append("\\r");
388                             continue;
389                         case '\t':
390                             buf.append("\\t");
391                             continue;
392                         case '\f':
393                             buf.append("\\f");
394                             continue;
395                         case '\b':
396                             buf.append("\\b");
397                             continue;
398 
399                         default:
400                             buf.append(c);
401                         continue;
402                     }
403                 }
404             }
405             
406             buf.append('"');
407         }     
408     }
409 
410     
411     /* ------------------------------------------------------------ */
412     /** Quote a string into a StringBuffer.
413      * The characters ", \, \n, \r, \t, \f, \b are escaped.
414      * Quotes are forced if any escaped characters are present or there
415      * is a ", ', space, +, =, ; or % character.
416      * 
417      * @param buf The StringBuffer
418      * @param s The String to quote.
419      */
420     public static void quoteIfNeeded(StringBuffer buf, String s)
421     {
422         synchronized(buf)
423         {
424             int e=-1;
425             
426             search: for (int i=0;i<s.length();i++)
427             {
428                 char c = s.charAt(i);
429                 switch(c)
430                 {
431                     case '"':
432                     case '\\':
433                     case '\n':
434                     case '\r':
435                     case '\t':
436                     case '\f':
437                     case '\b':
438                     case '%':
439                     case '+':
440                     case ' ':
441                     case ';':
442                     case '=':
443                         e=i;
444                         buf.append('"');
445                         // TODO when 1.4 support is dropped: buf.append(s,0,e);
446                         for (int j=0;j<e;j++)
447                             buf.append(s.charAt(j));
448                         break search;
449                         
450                     default:
451                         continue;
452                 }
453             }
454             
455             if (e<0)
456             {
457                 buf.append(s);
458                 return;
459             }
460             
461             for (int i=e;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             buf.append('"');
494         }
495     }
496     
497     /* ------------------------------------------------------------ */
498     /** Unquote a string.
499      * @param s The string to unquote.
500      * @return quoted string
501      */
502     public static String unquote(String s)
503     {
504         if (s==null)
505             return null;
506         if (s.length()<2)
507             return s;
508 
509         char first=s.charAt(0);
510         char last=s.charAt(s.length()-1);
511         if (first!=last || (first!='"' && first!='\''))
512             return s;
513         
514         StringBuffer b=new StringBuffer(s.length()-2);
515         synchronized(b)
516         {
517             boolean escape=false;
518             for (int i=1;i<s.length()-1;i++)
519             {
520                 char c = s.charAt(i);
521 
522                 if (escape)
523                 {
524                     escape=false;
525                     switch (c)
526                     {
527                         case 'n':
528                             b.append('\n');
529                             break;
530                         case 'r':
531                             b.append('\r');
532                             break;
533                         case 't':
534                             b.append('\t');
535                             break;
536                         case 'f':
537                             b.append('\f');
538                             break;
539                         case 'b':
540                             b.append('\b');
541                             break;
542                         case 'u':
543                             b.append((char)(
544                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
545                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
546                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
547                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
548                                     ) 
549                             );
550                             break;
551                         default:
552                             b.append(c);
553                     }
554                 }
555                 else if (c=='\\')
556                 {
557                     escape=true;
558                     continue;
559                 }
560                 else
561                     b.append(c);
562             }
563             
564             return b.toString();
565         }
566     }
567 
568     /* ------------------------------------------------------------ */
569     /**
570      * @return handle double quotes if true
571      */
572     public boolean getDouble()
573     {
574         return _double;
575     }
576 
577     /* ------------------------------------------------------------ */
578     /**
579      * @param d handle double quotes if true
580      */
581     public void setDouble(boolean d)
582     {
583         _double=d;
584     }
585 
586     /* ------------------------------------------------------------ */
587     /**
588      * @return handle single quotes if true
589      */
590     public boolean getSingle()
591     {
592         return _single;
593     }
594 
595     /* ------------------------------------------------------------ */
596     /**
597      * @param single handle single quotes if true
598      */
599     public void setSingle(boolean single)
600     {
601         _single=single;
602     }
603 }
604 
605 
606 
607 
608 
609 
610 
611 
612 
613 
614 
615