View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing,
    13   * software distributed under the License is distributed on an
    14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   * KIND, either express or implied.  See the License for the
    16   * specific language governing permissions and limitations
    17   * under the License.
    18   */
    19  package org.apache.myfaces.shared_orchestra.util;
    20
    21  import org.apache.myfaces.shared_orchestra.util.ArrayUtils;
    22
    23  import java.util.ArrayList;
    24
    25
    26  /**
    27   * Implements utility functions for the String class
    28   *
    29   * <p>
    30   * Emphasis on performance and reduced memory allocation/garbage collection
    31   * in exchange for longer more complex code.
    32   * </p>
    33   *
    34   * @author Anton Koinov (latest modification by $Author: skitching $)
    35   * @version $Revision: 673826 $ $Date: 2008-07-03 16:43:52 -0500 (Thu, 03 Jul 2008) $
    36   */
    37  public final class StringUtils
    38  {
    39      private StringUtils()
    40      {
    41          // utility class, no instantiation
    42      }
    43
    44      //~ Methods ------------------------------------------------------------------------------------
    45
    46      /**
    47       * Checks that the string represents a floating point number that CANNOT be
    48       * in exponential notation
    49       *
    50       * @param str the string to check
    51       *
    52       * @return boolean
    53       */
    54      public static boolean isFloatNoExponent(String str)
    55      {
    56          int len = str.length();
    57          if (len == 0)
    58          {
    59              return false;
    60          }
    61
    62          // skip first char if sign char
    63          char c = str.charAt(0);
    64          int  i = ((c == '-') || (c == '+')) ? 1 : 0;
    65
    66          // is it only a sign?
    67          if (i >= len)
    68          {
    69              return false;
    70          }
    71
    72          boolean decimalPointFound = false;
    73
    74          do
    75          {
    76              c = str.charAt(i);
    77              if (c == '.')
    78              {
    79                  // is this a second dot?
    80                  if (decimalPointFound)
    81                  {
    82                      return false;
    83                  }
    84
    85                  decimalPointFound = true;
    86              }
    87              else if (!Character.isDigit(c))
    88              {
    89                  return false;
    90              }
    91
    92              i++;
    93          }
    94          while (i < len);
    95
    96          return true;
    97      }
    98
    99      public static boolean isFloatWithOptionalExponent(String str)
    100     {
    101         int len = str.length();
    102         if (len == 0)
    103         {
    104             return false;
    105         }
    106
    107         // skip first char if sign char
    108         char c = str.charAt(0);
    109         int  i = ((c == '-') || (c == '+')) ? 1 : 0;
    110
    111         // is it only a sign?
    112         if (i >= len)
    113         {
    114             return false;
    115         }
    116
    117         boolean exponentFound     = false;
    118         boolean decimalPointFound = false;
    119
    120         do
    121         {
    122             c = str.charAt(i);
    123             switch (c)
    124             {
    125                 case '.':
    126
    127                     // is this a second one, are we in the exponent?
    128                     if (decimalPointFound || exponentFound)
    129                     {
    130                         return false;
    131                     }
    132                     decimalPointFound = true;
    133
    134                     break;
    135
    136                 case 'e':
    137                 case 'E':
    138
    139                     // is this a second one?
    140                     if (exponentFound)
    141                     {
    142                         return false;
    143                     }
    144                     exponentFound = true;
    145
    146                     // check for exponent sign
    147                     c = str.charAt(i + 1);
    148
    149                     if ((c == '-') || (c == '+'))
    150                     {
    151                         i++;
    152                     }
    153
    154                     break;
    155
    156                 default:
    157                     if (!Character.isDigit(c))
    158                     {
    159                         return false;
    160                     }
    161             }
    162
    163             i++;
    164         }
    165         while (i < len);
    166
    167         return true;
    168     }
    169
    170     public static boolean isInteger(String str)
    171     {
    172         int len = str.length();
    173         if (len == 0)
    174         {
    175             return false;
    176         }
    177
    178         // skip first char if sign char
    179         char c = str.charAt(0);
    180         int  i = ((c == '-') || (c == '+')) ? 1 : 0;
    181
    182         // is it only a sign?
    183         if (i >= len)
    184         {
    185             return false;
    186         }
    187
    188         do
    189         {
    190             if (!Character.isDigit(str.charAt(i)))
    191             {
    192                 return false;
    193             }
    194             i++;
    195         }
    196         while (i < len);
    197
    198         return true;
    199     }
    200
    201     public static boolean isUnsignedInteger(String str)
    202     {
    203         int len = str.length();
    204         if (len == 0)
    205         {
    206             return false;
    207         }
    208
    209         for (int i = 0; i < len; i++)
    210         {
    211             if (!Character.isDigit(str.charAt(i)))
    212             {
    213                 return false;
    214             }
    215         }
    216
    217         return true;
    218     }
    219
    220     /**
    221      * Undoubles the quotes inside the string <br> Example:<br>
    222      * <pre>
    223      * hello""world becomes hello"world
    224      * </pre>
    225      *
    226      * @param str input string to dequote
    227      * @param quote the quoting char
    228      *
    229      * @return dequoted string
    230      */
    231     public static String dequote(String str, char quote)
    232     {
    233         // Is there anything to dequote?
    234         if (str == null)
    235         {
    236             return null;
    237         }
    238
    239         return dequote(str, 0, str.length(), quote);
    240     }
    241
    242     /**
    243      * Undoubles the quotes inside a substring <br> Example:<br>
    244      * <pre>
    245      * hello""world becomes hello"world
    246      * </pre>
    247      * WARNING: scan for quote may continue to the end of the string, make sure
    248      * that either <code>charAt(end + 1) == quote</code> or <code>end =
    249      * str.lentgth()</code>. If in doubt call
    250      * <code>dequote(str.substring(begin, end), quote)</code>
    251      *
    252      * @param str input string from which to get the substring, must not be
    253      *        null
    254      * @param begin begin index for substring
    255      * @param end end index for substring
    256      * @param quote the quoting char
    257      *
    258      * @return dequoted string
    259      *
    260      * @throws IllegalArgumentException if string is incorrectly quoted
    261      */
    262     public static String dequote(String str, int begin, int end, char quote)
    263     {
    264         // Is there anything to dequote?
    265         if (begin == end)
    266         {
    267             return "";
    268         }
    269
    270         int end_ = str.indexOf(quote, begin);
    271
    272         // If no quotes, return the original string
    273         // and save StringBuffer allocation/char copying
    274         if (end_ < 0)
    275         {
    276             return str.substring(begin, end);
    277         }
    278
    279         StringBuffer sb     = new StringBuffer(end - begin);
    280         int          begin_ = begin; // need begin later
    281         for (; (end_ >= 0) && (end_ < end);
    282             end_ = str.indexOf(quote, begin_ = end_ + 2))
    283         {
    284             if (((end_ + 1) >= end) || (str.charAt(end_ + 1) != quote))
    285             {
    286                 throw new IllegalArgumentException(
    287                     "Internal quote not doubled in string '"
    288                     + str.substring(begin, end) + "'");
    289             }
    290
    291             sb.append(substring(str, begin_, end_)).append(quote);
    292         }
    293
    294         return sb.append(substring(str, begin_, end)).toString();
    295     }
    296
    297     /**
    298      * Removes the surrounding quote and any double quote inside the string <br>
    299      * Example:<br>
    300      * <pre>
    301      * "hello""world" becomes hello"world
    302      * </pre>
    303      *
    304      * @param str input string to dequote
    305      * @param quote the quoting char
    306      *
    307      * @return dequoted String
    308      */
    309     public static String dequoteFull(String str, char quote)
    310     {
    311         if (str == null)
    312         {
    313             return null;
    314         }
    315
    316         return dequoteFull(str, 0, str.length(), quote);
    317     }
    318
    319     public static String dequoteFull(String str, int begin, int end, char quote)
    320     {
    321         // If empty substring, return empty string
    322         if (begin == end)
    323         {
    324             return "";
    325         }
    326
    327         // If not quoted, return string
    328         if (str.charAt(begin) != quote)
    329         {
    330             return str.substring(begin, end);
    331         }
    332
    333         int _end = end - 1;
    334         if ((str.length() < 2) || (str.charAt(_end) != quote))
    335         {
    336             throw new IllegalArgumentException(
    337                 "Closing quote missing in string '"
    338                 + substring(str, begin, end) + "'");
    339         }
    340
    341         return dequote(str, begin + 1, _end, quote);
    342     }
    343
    344     public static String replace(String str, String repl, String with)
    345     {
    346         int lastindex = 0;
    347         int pos = str.indexOf(repl);
    348
    349         // If no replacement needed, return the original string
    350         // and save StringBuffer allocation/char copying
    351         if (pos < 0)
    352         {
    353             return str;
    354         }
    355
    356         int          len     = repl.length();
    357         int          lendiff = with.length() - repl.length();
    358         StringBuffer out     =
    359             new StringBuffer((lendiff <= 0) ? str.length()
    360                 : (str.length() + (10 * lendiff)));
    361         for (; pos >= 0; pos = str.indexOf(repl, lastindex = pos + len))
    362         {
    363             out.append(substring(str, lastindex, pos)).append(with);
    364         }
    365
    366         return out.append(substring(str, lastindex, str.length())).toString();
    367     }
    368
    369     public static String replace(String str, char repl, String with)
    370     {
    371         int pos = str.indexOf(repl);
    372
    373         // If no replacement needed, return the original string
    374         // and save StringBuffer allocation/char copying
    375         if (pos < 0)
    376         {
    377             return str;
    378         }
    379
    380         int          len       = str.length();
    381         int          lendiff   = with.length() - 1;
    382         StringBuffer out       =
    383             new StringBuffer((lendiff <= 0) ? str.length()
    384                 : (str.length() + (10 * lendiff)));
    385         int          lastindex = 0;
    386         for (; pos >= 0; pos = str.indexOf(repl, lastindex = pos + 1))
    387         {
    388             out.append(substring(str, lastindex, pos)).append(with);
    389         }
    390
    391         return out.append(substring(str, lastindex, len)).toString();
    392     }
    393
    394     public static StringBuffer replace(
    395         StringBuffer out, String s, String repl, String with)
    396     {
    397         int lastindex = 0;
    398         int len = repl.length();
    399         for (int index = s.indexOf(repl); index >= 0;
    400                     index = s.indexOf(repl, lastindex = index + len))
    401         {
    402             // we have search string at position index
    403             out.append(substring(s, lastindex, index)).append(with);
    404         }
    405
    406         return out.append(substring(s, lastindex, len));
    407     }
    408
    409     /**
    410      * Split a string into an array of strings arround a character separator.
    411      * This  function will be efficient for longer strings
    412      *
    413      * @param str the string to be split
    414      * @param separator the separator character
    415      *
    416      * @return array of string subparts
    417      */
    418     public static String[] splitLongString(String str, char separator)
    419     {
    420         int len;
    421         if (str == null || (len = str.length()) == 0)
    422         {
    423             return ArrayUtils.EMPTY_STRING_ARRAY;
    424         }
    425
    426         int       oldPos = 0;
    427         ArrayList list = new ArrayList();
    428         for (
    429             int pos = str.indexOf(separator); pos >= 0;
    430                     pos = str.indexOf(separator, (oldPos = (pos + 1))))
    431         {
    432             list.add(substring(str, oldPos, pos));
    433         }
    434
    435         list.add(substring(str, oldPos, len));
    436
    437         return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    438     }
    439
    440     /**
    441      * Split a string into an array of strings arround a character separator.
    442      * Each element can be optionally quoted by the quote character.<br>
    443      * This function will be efficient for long strings
    444      *
    445      * @param str the string to be split
    446      * @param separator the separator character
    447      * @param quote the quote character
    448      *
    449      * @return array of string subparts
    450      *
    451      * @throws IllegalArgumentException DOCUMENT ME!
    452      */
    453     public static String[] splitLongString(
    454         String str, char separator, char quote)
    455     {
    456         int len;
    457         if (str == null || (len = str.length()) == 0)
    458         {
    459             return ArrayUtils.EMPTY_STRING_ARRAY;
    460         }
    461
    462         int       oldPos = 0;
    463         ArrayList list = new ArrayList();
    464         for (int pos = 0; pos < len; oldPos = ++pos)
    465         {
    466             // Skip quoted text, if any
    467             while ((pos < len) && (str.charAt(pos) == quote))
    468             {
    469                 pos = str.indexOf(quote, pos + 1) + 1;
    470
    471                 if (pos == 0)
    472                 {
    473                     throw new IllegalArgumentException(
    474                         "Closing quote missing in string '" + str + "'");
    475                 }
    476             }
    477
    478             boolean quoted;
    479
    480             if (pos != oldPos)
    481             {
    482                 quoted = true;
    483
    484                 if ((pos < len) && (str.charAt(pos) != separator))
    485                 {
    486                     throw new IllegalArgumentException(
    487                         "Separator must follow closing quote in string '"
    488                         + str + "'");
    489                 }
    490             }
    491             else
    492             {
    493                 quoted     = false;
    494                 pos        = str.indexOf(separator, pos);
    495                 if (pos < 0)
    496                 {
    497                     pos = len;
    498                 }
    499             }
    500
    501             list.add(
    502                 quoted ? dequote(str, oldPos + 1, pos - 1, quote)
    503                     : substring(str, oldPos, pos));
    504         }
    505
    506         return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    507     }
    508
    509     /**
    510      * Split a string into an array of strings arround a character separator.
    511      * This  function will be efficient for short strings, for longer strings,
    512      * another approach may be better
    513      *
    514      * @param str the string to be split
    515      * @param separator the separator character
    516      *
    517      * @return array of string subparts
    518      */
    519     public static String[] splitShortString(String str, char separator)
    520     {
    521         int len;
    522         if (str == null || (len = str.length()) == 0)
    523         {
    524             return org.apache.myfaces.shared_orchestra.util.ArrayUtils.EMPTY_STRING_ARRAY;
    525         }
    526
    527         int lastTokenIndex = 0;
    528
    529         // Step 1: how many substrings?
    530         //      We exchange double scan time for less memory allocation
    531         for (int pos = str.indexOf(separator);
    532             pos >= 0; pos = str.indexOf(separator, pos + 1))
    533         {
    534             lastTokenIndex++;
    535         }
    536
    537         // Step 2: allocate exact size array
    538         String[] list   = new String[lastTokenIndex + 1];
    539
    540         int      oldPos = 0;
    541
    542         // Step 3: retrieve substrings
    543         for (
    544             int pos = str.indexOf(separator), i = 0; pos >= 0;
    545                     pos = str.indexOf(separator, (oldPos = (pos + 1))))
    546         {
    547             list[i++] = substring(str, oldPos, pos);
    548         }
    549
    550         list[lastTokenIndex] = substring(str, oldPos, len);
    551
    552         return list;
    553     }
    554
    555     /**
    556      * Split a string into an array of strings arround a character separator.
    557      * Each element can be optionally quoted by the quote character.<br>
    558      * This function will be efficient for short strings, for longer strings,
    559      * another approach may be better
    560      *
    561      * @param str the string to be split
    562      * @param separator the separator character
    563      * @param quote the quote character
    564      *
    565      * @return array of string subparts
    566      *
    567      * @throws IllegalArgumentException DOCUMENT ME!
    568      */
    569     public static String[] splitShortString(
    570         String str, char separator, char quote)
    571     {
    572         int len;
    573         if (str == null || (len = str.length()) == 0)
    574         {
    575             return ArrayUtils.EMPTY_STRING_ARRAY;
    576         }
    577
    578         // Step 1: how many substrings?
    579         //      We exchange double scan time for less memory allocation
    580         int tokenCount = 0;
    581         for (int pos = 0; pos < len; pos++)
    582         {
    583             tokenCount++;
    584
    585             int oldPos = pos;
    586
    587             // Skip quoted text, if any
    588             while ((pos < len) && (str.charAt(pos) == quote))
    589             {
    590                 pos = str.indexOf(quote, pos + 1) + 1;
    591
    592                 // pos == 0 is not found (-1 returned by indexOf + 1)
    593                 if (pos == 0)
    594                 {
    595                     throw new IllegalArgumentException(
    596                         "Closing quote missing in string '" + str + "'");
    597                 }
    598             }
    599
    600             if (pos != oldPos)
    601             {
    602                 if ((pos < len) && (str.charAt(pos) != separator))
    603                 {
    604                     throw new IllegalArgumentException(
    605                         "Separator must follow closing quote in strng '"
    606                         + str + "'");
    607                 }
    608             }
    609             else
    610             {
    611                 pos = str.indexOf(separator, pos);
    612                 if (pos < 0)
    613                 {
    614                     break;
    615                 }
    616             }
    617         }
    618
    619         // Main loop will finish one substring short when last char is separator
    620         if (str.charAt(len - 1) == separator)
    621         {
    622             tokenCount++;
    623         }
    624
    625         // Step 2: allocate exact size array
    626         String[] list = new String[tokenCount];
    627
    628         // Step 3: retrieve substrings
    629         // Note: on this pass we do not check for correctness,
    630         //       since we have already done so
    631         tokenCount--; // we want to stop one token short
    632
    633         int oldPos = 0;
    634         for (int pos = 0, i = 0; i < tokenCount; i++, oldPos = ++pos)
    635         {
    636             boolean quoted;
    637
    638             // Skip quoted text, if any
    639             while (str.charAt(pos) == quote)
    640             {
    641                 pos = str.indexOf(quote, pos + 1) + 1;
    642             }
    643
    644             if (pos != oldPos)
    645             {
    646                 quoted = true;
    647
    648                 if (str.charAt(pos) != separator)
    649                 {
    650                     throw new IllegalArgumentException(
    651                         "Separator must follow closing quote in strng '"
    652                         + str + "'");
    653                 }
    654             }
    655             else
    656             {
    657                 quoted     = false;
    658                 pos        = str.indexOf(separator, pos);
    659             }
    660
    661             list[i] =
    662                 quoted ? dequote(str, oldPos + 1, pos - 1, quote)
    663                     : substring(str, oldPos, pos);
    664         }
    665
    666         list[tokenCount] = dequoteFull(str, oldPos, len, quote);
    667
    668         return list;
    669     }
    670
    671     public static String substring(String str, int begin, int end)
    672     {
    673         if (begin == end)
    674         {
    675             return "";
    676         }
    677
    678         return str.substring(begin, end);
    679     }
    680
    681     public static String[] trim(String[] strings)
    682     {
    683         if (strings == null)
    684         {
    685             return null;
    686         }
    687
    688         for (int i = 0, len = strings.length; i < len; i++)
    689         {
    690             strings[i] = strings[i].trim();
    691         }
    692
    693         return strings;
    694     }
    695
    696     /**
    697      * Returns the minimum index >= 0, if any
    698      *
    699      * <p>
    700      * Use to find the first of two characters in a string:<br>
    701      * <code>minIndex(s.indexOf('/'), indexOf('\'))</code>
    702      * </p>
    703      *
    704      */
    705     public static int minIndex(int a, int b)
    706     {
    707         return (a < 0) ? b
    708             : (b < 0) ? a
    709                 : (a < b) ? a : b;
    710     }
    711 }