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