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             
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                         buf.append(s,0,i);
329                         buf.append("\\\"");
330                         break loop;
331                     case '\\':
332                         buf.append(s,0,i);
333                         buf.append("\\\\");
334                         break loop;
335                     case '\n':
336                         buf.append(s,0,i);
337                         buf.append("\\n");
338                         break loop;
339                     case '\r':
340                         buf.append(s,0,i);
341                         buf.append("\\r");
342                         break loop;
343                     case '\t':
344                         buf.append(s,0,i);
345                         buf.append("\\t");
346                         break loop;
347                     case '\f':
348                         buf.append(s,0,i);
349                         buf.append("\\f");
350                         break loop;
351                     case '\b':
352                         buf.append(s,0,i);
353                         buf.append("\\b");
354                         break loop;
355                         
356                     default:
357                         continue;
358                 }
359             }
360             if (i==s.length())
361                 buf.append(s);
362             else
363             {
364                 i++;
365                 for (;i<s.length();i++)
366                 {
367                     char c = s.charAt(i);
368                     switch(c)
369                     {
370                         case '"':
371                             buf.append("\\\"");
372                             continue;
373                         case '\\':
374                             buf.append("\\\\");
375                             continue;
376                         case '\n':
377                             buf.append("\\n");
378                             continue;
379                         case '\r':
380                             buf.append("\\r");
381                             continue;
382                         case '\t':
383                             buf.append("\\t");
384                             continue;
385                         case '\f':
386                             buf.append("\\f");
387                             continue;
388                         case '\b':
389                             buf.append("\\b");
390                             continue;
391 
392                         default:
393                             buf.append(c);
394                         continue;
395                     }
396                 }
397             }
398             
399             buf.append('"');
400         } 
401         
402         
403         
404     }
405 
406     
407     /* ------------------------------------------------------------ */
408     /** Quote a string into a StringBuffer.
409      * The characters ", \, \n, \r, \t, \f, \b are escaped.
410      * Quotes are forced if any escaped characters are present or there
411      * is a ", ', space, + or % character.
412      * 
413      * @param buf The StringBuffer
414      * @param s The String to quote.
415      */
416     public static void quoteIfNeeded(StringBuffer buf, String s)
417     {
418         synchronized(buf)
419         {
420             int e=-1;
421             
422             search: for (int i=0;i<s.length();i++)
423             {
424                 char c = s.charAt(i);
425                 switch(c)
426                 {
427                     case '"':
428                     case '\\':
429                     case '\n':
430                     case '\r':
431                     case '\t':
432                     case '\f':
433                     case '\b':
434                     case '%':
435                     case '+':
436                     case ' ':
437                         e=i;
438                         buf.append('"');
439                         // TODO when 1.4 support is dropped: buf.append(s,0,e);
440                         for (int j=0;j<e;j++)
441                             buf.append(s.charAt(j));
442                         break search;
443                         
444                     default:
445                         continue;
446                 }
447             }
448             
449             if (e<0)
450             {
451                 buf.append(s);
452                 return;
453             }
454             
455             for (int i=e;i<s.length();i++)
456             {
457                 char c = s.charAt(i);
458                 switch(c)
459                 {
460                     case '"':
461                         buf.append("\\\"");
462                         continue;
463                     case '\\':
464                         buf.append("\\\\");
465                         continue;
466                     case '\n':
467                         buf.append("\\n");
468                         continue;
469                     case '\r':
470                         buf.append("\\r");
471                         continue;
472                     case '\t':
473                         buf.append("\\t");
474                         continue;
475                     case '\f':
476                         buf.append("\\f");
477                         continue;
478                     case '\b':
479                         buf.append("\\b");
480                         continue;
481                         
482                     default:
483                         buf.append(c);
484                         continue;
485                 }
486             }
487             buf.append('"');
488         }
489     }
490     
491     /* ------------------------------------------------------------ */
492     /** Unquote a string.
493      * @param s The string to unquote.
494      * @return quoted string
495      */
496     public static String unquote(String s)
497     {
498         if (s==null)
499             return null;
500         if (s.length()<2)
501             return s;
502 
503         char first=s.charAt(0);
504         char last=s.charAt(s.length()-1);
505         if (first!=last || (first!='"' && first!='\''))
506             return s;
507         
508         StringBuffer b=new StringBuffer(s.length()-2);
509         synchronized(b)
510         {
511             boolean escape=false;
512             for (int i=1;i<s.length()-1;i++)
513             {
514                 char c = s.charAt(i);
515 
516                 if (escape)
517                 {
518                     escape=false;
519                     switch (c)
520                     {
521                         case 'n':
522                             b.append('\n');
523                             break;
524                         case 'r':
525                             b.append('\r');
526                             break;
527                         case 't':
528                             b.append('\t');
529                             break;
530                         case 'f':
531                             b.append('\f');
532                             break;
533                         case 'b':
534                             b.append('\b');
535                             break;
536                         case 'u':
537                             b.append((char)(
538                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
539                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
540                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
541                                     (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
542                                     ) 
543                             );
544                             break;
545                         default:
546                             b.append(c);
547                     }
548                 }
549                 else if (c=='\\')
550                 {
551                     escape=true;
552                     continue;
553                 }
554                 else
555                     b.append(c);
556             }
557             
558             return b.toString();
559         }
560     }
561 
562     /* ------------------------------------------------------------ */
563     /**
564      * @return handle double quotes if true
565      */
566     public boolean getDouble()
567     {
568         return _double;
569     }
570 
571     /* ------------------------------------------------------------ */
572     /**
573      * @param d handle double quotes if true
574      */
575     public void setDouble(boolean d)
576     {
577         _double=d;
578     }
579 
580     /* ------------------------------------------------------------ */
581     /**
582      * @return handle single quotes if true
583      */
584     public boolean getSingle()
585     {
586         return _single;
587     }
588 
589     /* ------------------------------------------------------------ */
590     /**
591      * @param single handle single quotes if true
592      */
593     public void setSingle(boolean single)
594     {
595         _single=single;
596     }
597 }
598 
599 
600 
601 
602 
603 
604 
605 
606 
607 
608 
609