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