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                         e=i;
442                         buf.append('"');
443                         // TODO when 1.4 support is dropped: buf.append(s,0,e);
444                         for (int j=0;j<e;j++)
445                             buf.append(s.charAt(j));
446                         break search;
447                         
448                     default:
449                         continue;
450                 }
451             }
452             
453             if (e<0)
454             {
455                 buf.append(s);
456                 return;
457             }
458             
459             for (int i=e;i<s.length();i++)
460             {
461                 char c = s.charAt(i);
462                 switch(c)
463                 {
464                     case '"':
465                         buf.append("\\\"");
466                         continue;
467                     case '\\':
468                         buf.append("\\\\");
469                         continue;
470                     case '\n':
471                         buf.append("\\n");
472                         continue;
473                     case '\r':
474                         buf.append("\\r");
475                         continue;
476                     case '\t':
477                         buf.append("\\t");
478                         continue;
479                     case '\f':
480                         buf.append("\\f");
481                         continue;
482                     case '\b':
483                         buf.append("\\b");
484                         continue;
485                         
486                     default:
487                         buf.append(c);
488                         continue;
489                 }
490             }
491             buf.append('"');
492         }
493     }
494     
495     /* ------------------------------------------------------------ */
496     /** Unquote a string.
497      * @param s The string to unquote.
498      * @return quoted string
499      */
500     public static String unquote(String s)
501     {
502         if (s==null)
503             return null;
504         if (s.length()<2)
505             return s;
506 
507         char first=s.charAt(0);
508         char last=s.charAt(s.length()-1);
509         if (first!=last || (first!='"' && first!='\''))
510             return s;
511         
512         StringBuffer b=new StringBuffer(s.length()-2);
513         synchronized(b)
514         {
515             boolean escape=false;
516             for (int i=1;i<s.length()-1;i++)
517             {
518                 char c = s.charAt(i);
519 
520                 if (escape)
521                 {
522                     escape=false;
523                     switch (c)
524                     {
525                         case 'n':
526                             b.append('\n');
527                             break;
528                         case 'r':
529                             b.append('\r');
530                             break;
531                         case 't':
532                             b.append('\t');
533                             break;
534                         case 'f':
535                             b.append('\f');
536                             break;
537                         case 'b':
538                             b.append('\b');
539                             break;
540                         case 'u':
541                             b.append((char)(
542                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
543                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
544                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
545                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
546                                     ) 
547                             );
548                             break;
549                         default:
550                             b.append(c);
551                     }
552                 }
553                 else if (c=='\\')
554                 {
555                     escape=true;
556                     continue;
557                 }
558                 else
559                     b.append(c);
560             }
561             
562             return b.toString();
563         }
564     }
565 
566     /* ------------------------------------------------------------ */
567     /**
568      * @return handle double quotes if true
569      */
570     public boolean getDouble()
571     {
572         return _double;
573     }
574 
575     /* ------------------------------------------------------------ */
576     /**
577      * @param d handle double quotes if true
578      */
579     public void setDouble(boolean d)
580     {
581         _double=d;
582     }
583 
584     /* ------------------------------------------------------------ */
585     /**
586      * @return handle single quotes if true
587      */
588     public boolean getSingle()
589     {
590         return _single;
591     }
592 
593     /* ------------------------------------------------------------ */
594     /**
595      * @param single handle single quotes if true
596      */
597     public void setSingle(boolean single)
598     {
599         _single=single;
600     }
601 }
602 
603 
604 
605 
606 
607 
608 
609 
610 
611 
612 
613