PageRenderTime 63ms CodeModel.GetById 10ms app.highlight 47ms RepoModel.GetById 2ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/util/QuotedStringTokenizer.java

http://github.com/hudson/hudson
Java | 565 lines | 472 code | 15 blank | 78 comment | 11 complexity | 064a15392eec1759bbdadb06f765ce66 MD5 | raw file
  1/**
  2 * (C) Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
  3 *
  4 *   Parts of this code was taken from the Jetty project, which can be
  5 *   found at http://www.mortbay.org/jetty
  6 *
  7 * Licensed to the Apache Software Foundation (ASF) under one or more
  8 * contributor license agreements.  See the NOTICE file distributed with
  9 * this work for additional information regarding copyright ownership.
 10 * The ASF licenses this file to You under the Apache License, Version 2.0
 11 * (the "License"); you may not use this file except in compliance with
 12 * the License.  You may obtain a copy of the License at
 13 *
 14 * http://www.apache.org/licenses/LICENSE-2.0
 15 *
 16 * Unless required by applicable law or agreed to in writing, software
 17 * distributed under the License is distributed on an "AS IS" BASIS,
 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 19 * See the License for the specific language governing permissions and
 20 * limitations under the License.
 21 */
 22
 23// ========================================================================
 24// Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
 25// ------------------------------------------------------------------------
 26// Licensed under the Apache License, Version 2.0 (the "License");
 27// you may not use this file except in compliance with the License.
 28// You may obtain a copy of the License at
 29// http://www.apache.org/licenses/LICENSE-2.0
 30// Unless required by applicable law or agreed to in writing, software
 31// distributed under the License is distributed on an "AS IS" BASIS,
 32// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 33// See the License for the specific language governing permissions and
 34// limitations under the License.
 35// ========================================================================
 36package hudson.util;
 37
 38import java.util.NoSuchElementException;
 39import java.util.StringTokenizer;
 40import java.util.List;
 41import java.util.ArrayList;
 42
 43/* ------------------------------------------------------------ */
 44/** StringTokenizer with Quoting support.
 45 *
 46 * This class is a copy of the java.util.StringTokenizer API and
 47 * the behaviour is the same, except that single and doulbe quoted
 48 * string values are recognized.
 49 * Delimiters within quotes are not considered delimiters.
 50 * Quotes can be escaped with '\'.
 51 *
 52 * @see java.util.StringTokenizer
 53 * @author Greg Wilkins (gregw)
 54 */
 55public class QuotedStringTokenizer
 56    extends StringTokenizer
 57{
 58    private final static String __delim=" \t\n\r";
 59    private String _string;
 60    private String _delim = __delim;
 61    private boolean _returnQuotes=false;
 62    private boolean _returnDelimiters=false;
 63    private StringBuffer _token;
 64    private boolean _hasToken=false;
 65    private int _i=0;
 66    private int _lastStart=0;
 67    private boolean _double=true;
 68    private boolean _single=true;
 69
 70    public static String[] tokenize(String str) {
 71        return new QuotedStringTokenizer(str).toArray();
 72    }
 73
 74    public static String[] tokenize(String str, String delimiters) {
 75        return new QuotedStringTokenizer(str,delimiters).toArray();
 76    }
 77
 78    /* ------------------------------------------------------------ */
 79    /**
 80     *
 81     * @param str
 82     *      String to tokenize.
 83     * @param delim
 84     *      List of delimiter characters as string. Can be null, to default to ' \t\n\r'
 85     * @param returnDelimiters
 86     *      If true, {@link #nextToken()} will include the delimiters, not just tokenized
 87     *      tokens.
 88     * @param returnQuotes
 89     *      If true, {@link #nextToken()} will include the quotation characters when they are present.
 90     */
 91    public QuotedStringTokenizer(String str,
 92                                 String delim,
 93                                 boolean returnDelimiters,
 94                                 boolean returnQuotes)
 95    {
 96        super("");
 97        _string=str;
 98        if (delim!=null)
 99            _delim=delim;
100        _returnDelimiters=returnDelimiters;
101        _returnQuotes=returnQuotes;
102
103        if (_delim.indexOf('\'')>=0 ||
104            _delim.indexOf('"')>=0)
105            throw new Error("Can't use quotes as delimiters: "+_delim);
106
107        _token=new StringBuffer(_string.length()>1024?512:_string.length()/2);
108    }
109
110    /* ------------------------------------------------------------ */
111    public QuotedStringTokenizer(String str,
112                                 String delim,
113                                 boolean returnDelimiters)
114    {
115        this(str,delim,returnDelimiters,false);
116    }
117
118    /* ------------------------------------------------------------ */
119    public QuotedStringTokenizer(String str,
120                                 String delim)
121    {
122        this(str,delim,false,false);
123    }
124
125    /* ------------------------------------------------------------ */
126    public QuotedStringTokenizer(String str)
127    {
128        this(str,null,false,false);
129    }
130
131    public String[] toArray() {
132        List<String> r = new ArrayList<String>();
133        while(hasMoreTokens())
134            r.add(nextToken());
135        return r.toArray(new String[r.size()]);
136    }
137
138
139    /* ------------------------------------------------------------ */
140    @Override
141    public boolean hasMoreTokens()
142    {
143        // Already found a token
144        if (_hasToken)
145            return true;
146
147        _lastStart=_i;
148
149        int state=0;
150        boolean escape=false;
151        while (_i<_string.length())
152        {
153            char c=_string.charAt(_i++);
154
155            switch (state)
156            {
157              case 0: // Start
158                  if(_delim.indexOf(c)>=0)
159                  {
160                      if (_returnDelimiters)
161                      {
162                          _token.append(c);
163                          return _hasToken=true;
164                      }
165                  }
166                  else if (c=='\'' && _single)
167                  {
168                      if (_returnQuotes)
169                          _token.append(c);
170                      state=2;
171                  }
172                  else if (c=='\"' && _double)
173                  {
174                      if (_returnQuotes)
175                          _token.append(c);
176                      state=3;
177                  }
178                  else
179                  {
180                      _token.append(c);
181                      _hasToken=true;
182                      state=1;
183                  }
184                  continue;
185
186              case 1: // Token
187                  _hasToken=true;
188                  if (escape)
189                  {
190                      escape=false;
191                      if(ESCAPABLE_CHARS.indexOf(c)<0)
192                          _token.append('\\');
193                      _token.append(c);
194                  }
195                  else if(_delim.indexOf(c)>=0)
196                  {
197                      if (_returnDelimiters)
198                          _i--;
199                      return _hasToken;
200                  }
201                  else if (c=='\'' && _single)
202                  {
203                      if (_returnQuotes)
204                          _token.append(c);
205                      state=2;
206                  }
207                  else if (c=='\"' && _double)
208                  {
209                      if (_returnQuotes)
210                          _token.append(c);
211                      state=3;
212                  }
213                  else if (c=='\\')
214                  {
215                      escape=true;
216                  }
217                  else
218                      _token.append(c);
219                  continue;
220
221
222              case 2: // Single Quote
223                  _hasToken=true;
224                  if (escape)
225                  {
226                      escape=false;
227                      if(ESCAPABLE_CHARS.indexOf(c)<0)
228                          _token.append('\\');
229                      _token.append(c);
230                  }
231                  else if (c=='\'')
232                  {
233                      if (_returnQuotes)
234                          _token.append(c);
235                      state=1;
236                  }
237                  else if (c=='\\')
238                  {
239                      if (_returnQuotes)
240                          _token.append(c);
241                      escape=true;
242                  }
243                  else
244                      _token.append(c);
245                  continue;
246
247
248              case 3: // Double Quote
249                  _hasToken=true;
250                  if (escape)
251                  {
252                      escape=false;
253                      if(ESCAPABLE_CHARS.indexOf(c)<0)
254                          _token.append('\\');
255                      _token.append(c);
256                  }
257                  else if (c=='\"')
258                  {
259                      if (_returnQuotes)
260                          _token.append(c);
261                      state=1;
262                  }
263                  else if (c=='\\')
264                  {
265                      if (_returnQuotes)
266                          _token.append(c);
267                      escape=true;
268                  }
269                  else
270                      _token.append(c);
271                  continue;
272            }
273        }
274
275        return _hasToken;
276    }
277
278    /* ------------------------------------------------------------ */
279    @Override
280    public String nextToken()
281        throws NoSuchElementException
282    {
283        if (!hasMoreTokens() || _token==null)
284            throw new NoSuchElementException();
285        String t=_token.toString();
286        _token.setLength(0);
287        _hasToken=false;
288        return t;
289    }
290
291    /* ------------------------------------------------------------ */
292    @Override
293    public String nextToken(String delim)
294        throws NoSuchElementException
295    {
296        _delim=delim;
297        _i=_lastStart;
298        _token.setLength(0);
299        _hasToken=false;
300        return nextToken();
301    }
302
303    /* ------------------------------------------------------------ */
304    @Override
305    public boolean hasMoreElements()
306    {
307        return hasMoreTokens();
308    }
309
310    /* ------------------------------------------------------------ */
311    @Override
312    public Object nextElement()
313        throws NoSuchElementException
314    {
315        return nextToken();
316    }
317
318    /* ------------------------------------------------------------ */
319    /** Not implemented.
320     */
321    @Override
322    public int countTokens()
323    {
324        return -1;
325    }
326
327
328    /* ------------------------------------------------------------ */
329    /** Quote a string.
330     * The string is quoted only if quoting is required due to
331     * embeded delimiters, quote characters or the
332     * empty string.
333     * @param s The string to quote.
334     * @return quoted string
335     */
336    public static String quote(String s, String delim)
337    {
338        if (s==null)
339            return null;
340        if (s.length()==0)
341            return "\"\"";
342
343
344        for (int i=0;i<s.length();i++)
345        {
346            char c = s.charAt(i);
347            if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0)
348            {
349                StringBuffer b=new StringBuffer(s.length()+8);
350                quote(b,s);
351                return b.toString();
352            }
353        }
354
355        return s;
356    }
357
358    /* ------------------------------------------------------------ */
359    /** Quote a string.
360     * The string is quoted only if quoting is required due to
361     * embeded delimiters, quote characters or the
362     * empty string.
363     * @param s The string to quote.
364     * @return quoted string
365     */
366    public static String quote(String s)
367    {
368        if (s==null)
369            return null;
370        if (s.length()==0)
371            return "\"\"";
372
373        StringBuffer b=new StringBuffer(s.length()+8);
374        quote(b,s);
375        return b.toString();
376
377    }
378
379
380    /* ------------------------------------------------------------ */
381    /** Quote a string into a StringBuffer.
382     * The characters ", \, \n, \r, \t, \f and \b are escaped
383     * @param buf The StringBuffer
384     * @param s The String to quote.
385     */
386    public static void quote(StringBuffer buf, String s)
387    {
388        synchronized(buf)
389        {
390            buf.append('"');
391            for (int i=0;i<s.length();i++)
392            {
393                char c = s.charAt(i);
394                switch(c)
395                {
396                    case '"':
397                        buf.append("\\\"");
398                        continue;
399                    case '\\':
400                        buf.append("\\\\");
401                        continue;
402                    case '\n':
403                        buf.append("\\n");
404                        continue;
405                    case '\r':
406                        buf.append("\\r");
407                        continue;
408                    case '\t':
409                        buf.append("\\t");
410                        continue;
411                    case '\f':
412                        buf.append("\\f");
413                        continue;
414                    case '\b':
415                        buf.append("\\b");
416                        continue;
417
418                    default:
419                        buf.append(c);
420                        continue;
421                }
422            }
423            buf.append('"');
424        }
425    }
426
427    /* ------------------------------------------------------------ */
428    /** Unquote a string.
429     * @param s The string to unquote.
430     * @return quoted string
431     */
432    public static String unquote(String s)
433    {
434        if (s==null)
435            return null;
436        if (s.length()<2)
437            return s;
438
439        char first=s.charAt(0);
440        char last=s.charAt(s.length()-1);
441        if (first!=last || (first!='"' && first!='\''))
442            return s;
443
444        StringBuffer b=new StringBuffer(s.length()-2);
445        synchronized(b)
446        {
447            boolean escape=false;
448            for (int i=1;i<s.length()-1;i++)
449            {
450                char c = s.charAt(i);
451
452                if (escape)
453                {
454                    escape=false;
455                    switch (c)
456                    {
457                        case 'n':
458                            b.append('\n');
459                            break;
460                        case 'r':
461                            b.append('\r');
462                            break;
463                        case 't':
464                            b.append('\t');
465                            break;
466                        case 'f':
467                            b.append('\f');
468                            break;
469                        case 'b':
470                            b.append('\b');
471                            break;
472                        case 'u':
473                            b.append((char)(
474                                    (convertHexDigit((byte)s.charAt(i++))<<24)+
475                                    (convertHexDigit((byte)s.charAt(i++))<<16)+
476                                    (convertHexDigit((byte)s.charAt(i++))<<8)+
477                                    (convertHexDigit((byte)s.charAt(i++)))
478                                    )
479                            );
480                            break;
481                        default:
482                            b.append(c);
483                    }
484                }
485                else if (c=='\\')
486                {
487                    escape=true;
488                    continue;
489                }
490                else
491                    b.append(c);
492            }
493
494            return b.toString();
495        }
496    }
497
498    /* ------------------------------------------------------------ */
499    /**
500     * @return handle double quotes if true
501     */
502    public boolean getDouble()
503    {
504        return _double;
505    }
506
507    /* ------------------------------------------------------------ */
508    /**
509     * @param d handle double quotes if true
510     */
511    public void setDouble(boolean d)
512    {
513        _double=d;
514    }
515
516    /* ------------------------------------------------------------ */
517    /**
518     * @return handle single quotes if true
519     */
520    public boolean getSingle()
521    {
522        return _single;
523    }
524
525    /* ------------------------------------------------------------ */
526    /**
527     * @param single handle single quotes if true
528     */
529    public void setSingle(boolean single)
530    {
531        _single=single;
532    }
533
534    /**
535     * @param b An ASCII encoded character 0-9 a-f A-F
536     * @return The byte value of the character 0-16.
537     */
538    public static byte convertHexDigit( byte b )
539    {
540        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
541        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
542        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
543        return 0;
544    }
545
546    /**
547     * Characters that can be escaped with \.
548     *
549     * Others, like, say, \W will be left alone instead of becoming just W.
550     * This is important to keep Hudson behave on Windows, which uses '\' as
551     * the directory separator. 
552     */
553    private static final String ESCAPABLE_CHARS = "\\\"' ";
554}
555
556
557
558
559
560
561
562
563
564
565