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