Coverage Report - org.apache.shiro.util.StringUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
StringUtils
57%
80/140
49%
56/114
4.842
 
 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.shiro.util;
 20  
 
 21  
 import java.text.ParseException;
 22  
 import java.util.*;
 23  
 
 24  
 /**
 25  
  * <p>Simple utility class for String operations useful across the framework.
 26  
  * <p/>
 27  
  * <p>Some methods in this class were copied from the Spring Framework so we didn't have to re-invent the wheel,
 28  
  * and in these cases, we have retained all license, copyright and author information.
 29  
  *
 30  
  * @since 0.9
 31  
  */
 32  0
 public class StringUtils {
 33  
 
 34  
     //TODO - complete JavaDoc
 35  
 
 36  
     /**
 37  
      * Constant representing the empty string, equal to &quot;&quot;
 38  
      */
 39  
     public static final String EMPTY_STRING = "";
 40  
 
 41  
     /**
 42  
      * Constant representing the default delimiter character (comma), equal to <code>','</code>
 43  
      */
 44  
     public static final char DEFAULT_DELIMITER_CHAR = ',';
 45  
 
 46  
     /**
 47  
      * Constant representing the default quote character (double quote), equal to '&quot;'</code>
 48  
      */
 49  
     public static final char DEFAULT_QUOTE_CHAR = '"';
 50  
 
 51  
     /**
 52  
      * Check whether the given String has actual text.
 53  
      * More specifically, returns <code>true</code> if the string not <code>null</code>,
 54  
      * its length is greater than 0, and it contains at least one non-whitespace character.
 55  
      * <p/>
 56  
      * <code>StringUtils.hasText(null) == false<br/>
 57  
      * StringUtils.hasText("") == false<br/>
 58  
      * StringUtils.hasText(" ") == false<br/>
 59  
      * StringUtils.hasText("12345") == true<br/>
 60  
      * StringUtils.hasText(" 12345 ") == true</code>
 61  
      * <p/>
 62  
      * <p>Copied from the Spring Framework while retaining all license, copyright and author information.
 63  
      *
 64  
      * @param str the String to check (may be <code>null</code>)
 65  
      * @return <code>true</code> if the String is not <code>null</code>, its length is
 66  
      *         greater than 0, and it does not contain whitespace only
 67  
      * @see java.lang.Character#isWhitespace
 68  
      */
 69  
     public static boolean hasText(String str) {
 70  296
         if (!hasLength(str)) {
 71  27
             return false;
 72  
         }
 73  269
         int strLen = str.length();
 74  327
         for (int i = 0; i < strLen; i++) {
 75  326
             if (!Character.isWhitespace(str.charAt(i))) {
 76  268
                 return true;
 77  
             }
 78  
         }
 79  1
         return false;
 80  
     }
 81  
 
 82  
     /**
 83  
      * Check that the given String is neither <code>null</code> nor of length 0.
 84  
      * Note: Will return <code>true</code> for a String that purely consists of whitespace.
 85  
      * <p/>
 86  
      * <code>StringUtils.hasLength(null) == false<br/>
 87  
      * StringUtils.hasLength("") == false<br/>
 88  
      * StringUtils.hasLength(" ") == true<br/>
 89  
      * StringUtils.hasLength("Hello") == true</code>
 90  
      * <p/>
 91  
      * Copied from the Spring Framework while retaining all license, copyright and author information.
 92  
      *
 93  
      * @param str the String to check (may be <code>null</code>)
 94  
      * @return <code>true</code> if the String is not null and has length
 95  
      * @see #hasText(String)
 96  
      */
 97  
     public static boolean hasLength(String str) {
 98  308
         return (str != null && str.length() > 0);
 99  
     }
 100  
 
 101  
 
 102  
     /**
 103  
      * Test if the given String starts with the specified prefix,
 104  
      * ignoring upper/lower case.
 105  
      * <p/>
 106  
      * <p>Copied from the Spring Framework while retaining all license, copyright and author information.
 107  
      *
 108  
      * @param str    the String to check
 109  
      * @param prefix the prefix to look for
 110  
      * @return <code>true</code> starts with the specified prefix (ignoring case), <code>false</code> if it does not.
 111  
      * @see java.lang.String#startsWith
 112  
      */
 113  
     public static boolean startsWithIgnoreCase(String str, String prefix) {
 114  0
         if (str == null || prefix == null) {
 115  0
             return false;
 116  
         }
 117  0
         if (str.startsWith(prefix)) {
 118  0
             return true;
 119  
         }
 120  0
         if (str.length() < prefix.length()) {
 121  0
             return false;
 122  
         }
 123  0
         String lcStr = str.substring(0, prefix.length()).toLowerCase();
 124  0
         String lcPrefix = prefix.toLowerCase();
 125  0
         return lcStr.equals(lcPrefix);
 126  
     }
 127  
 
 128  
     /**
 129  
      * Returns a 'cleaned' representation of the specified argument.  'Cleaned' is defined as the following:
 130  
      * <p/>
 131  
      * <ol>
 132  
      * <li>If the specified <code>String</code> is <code>null</code>, return <code>null</code></li>
 133  
      * <li>If not <code>null</code>, {@link String#trim() trim()} it.</li>
 134  
      * <li>If the trimmed string is equal to the empty String (i.e. &quot;&quot;), return <code>null</code></li>
 135  
      * <li>If the trimmed string is not the empty string, return the trimmed version</li>.
 136  
      * </ol>
 137  
      * <p/>
 138  
      * Therefore this method always ensures that any given string has trimmed text, and if it doesn't, <code>null</code>
 139  
      * is returned.
 140  
      *
 141  
      * @param in the input String to clean.
 142  
      * @return a populated-but-trimmed String or <code>null</code> otherwise
 143  
      */
 144  
     public static String clean(String in) {
 145  946
         String out = in;
 146  
 
 147  946
         if (in != null) {
 148  944
             out = in.trim();
 149  944
             if (out.equals(EMPTY_STRING)) {
 150  28
                 out = null;
 151  
             }
 152  
         }
 153  
 
 154  946
         return out;
 155  
     }
 156  
 
 157  
     /**
 158  
      * Returns the specified array as a comma-delimited (',') string.
 159  
      *
 160  
      * @param array the array whose contents will be converted to a string.
 161  
      * @return the array's contents as a comma-delimited (',') string.
 162  
      * @since 1.0
 163  
      */
 164  
     public static String toString(Object[] array) {
 165  34
         return toDelimitedString(array, ",");
 166  
     }
 167  
 
 168  
     /**
 169  
      * Returns the array's contents as a string, with each element delimited by the specified
 170  
      * {@code delimiter} argument.  Useful for {@code toString()} implementations and log messages.
 171  
      *
 172  
      * @param array     the array whose contents will be converted to a string
 173  
      * @param delimiter the delimiter to use between each element
 174  
      * @return a single string, delimited by the specified {@code delimiter}.
 175  
      * @since 1.0
 176  
      */
 177  
     public static String toDelimitedString(Object[] array, String delimiter) {
 178  34
         if (array == null || array.length == 0) {
 179  0
             return EMPTY_STRING;
 180  
         }
 181  34
         StringBuilder sb = new StringBuilder();
 182  71
         for (int i = 0; i < array.length; i++) {
 183  37
             if (i > 0) {
 184  3
                 sb.append(delimiter);
 185  
             }
 186  37
             sb.append(array[i]);
 187  
         }
 188  34
         return sb.toString();
 189  
     }
 190  
 
 191  
     /**
 192  
      * Returns the collection's contents as a string, with each element delimited by the specified
 193  
      * {@code delimiter} argument.  Useful for {@code toString()} implementations and log messages.
 194  
      *
 195  
      * @param c         the collection whose contents will be converted to a string
 196  
      * @param delimiter the delimiter to use between each element
 197  
      * @return a single string, delimited by the specified {@code delimiter}.
 198  
      * @since 1.2
 199  
      */
 200  
     public static String toDelimitedString(Collection c, String delimiter) {
 201  0
         if (c == null || c.isEmpty()) {
 202  0
             return EMPTY_STRING;
 203  
         }
 204  0
         return join(c.iterator(), delimiter);
 205  
     }
 206  
 
 207  
     /**
 208  
      * Tokenize the given String into a String array via a StringTokenizer.
 209  
      * Trims tokens and omits empty tokens.
 210  
      * <p>The given delimiters string is supposed to consist of any number of
 211  
      * delimiter characters. Each of those characters can be used to separate
 212  
      * tokens. A delimiter is always a single character; for multi-character
 213  
      * delimiters, consider using <code>delimitedListToStringArray</code>
 214  
      * <p/>
 215  
      * <p>Copied from the Spring Framework while retaining all license, copyright and author information.
 216  
      *
 217  
      * @param str        the String to tokenize
 218  
      * @param delimiters the delimiter characters, assembled as String
 219  
      *                   (each of those characters is individually considered as delimiter).
 220  
      * @return an array of the tokens
 221  
      * @see java.util.StringTokenizer
 222  
      * @see java.lang.String#trim()
 223  
      */
 224  
     public static String[] tokenizeToStringArray(String str, String delimiters) {
 225  0
         return tokenizeToStringArray(str, delimiters, true, true);
 226  
     }
 227  
 
 228  
     /**
 229  
      * Tokenize the given String into a String array via a StringTokenizer.
 230  
      * <p>The given delimiters string is supposed to consist of any number of
 231  
      * delimiter characters. Each of those characters can be used to separate
 232  
      * tokens. A delimiter is always a single character; for multi-character
 233  
      * delimiters, consider using <code>delimitedListToStringArray</code>
 234  
      * <p/>
 235  
      * <p>Copied from the Spring Framework while retaining all license, copyright and author information.
 236  
      *
 237  
      * @param str               the String to tokenize
 238  
      * @param delimiters        the delimiter characters, assembled as String
 239  
      *                          (each of those characters is individually considered as delimiter)
 240  
      * @param trimTokens        trim the tokens via String's <code>trim</code>
 241  
      * @param ignoreEmptyTokens omit empty tokens from the result array
 242  
      *                          (only applies to tokens that are empty after trimming; StringTokenizer
 243  
      *                          will not consider subsequent delimiters as token in the first place).
 244  
      * @return an array of the tokens (<code>null</code> if the input String
 245  
      *         was <code>null</code>)
 246  
      * @see java.util.StringTokenizer
 247  
      * @see java.lang.String#trim()
 248  
      */
 249  
     @SuppressWarnings({"unchecked"})
 250  
     public static String[] tokenizeToStringArray(
 251  
             String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
 252  
 
 253  0
         if (str == null) {
 254  0
             return null;
 255  
         }
 256  0
         StringTokenizer st = new StringTokenizer(str, delimiters);
 257  0
         List tokens = new ArrayList();
 258  0
         while (st.hasMoreTokens()) {
 259  0
             String token = st.nextToken();
 260  0
             if (trimTokens) {
 261  0
                 token = token.trim();
 262  
             }
 263  0
             if (!ignoreEmptyTokens || token.length() > 0) {
 264  0
                 tokens.add(token);
 265  
             }
 266  0
         }
 267  0
         return toStringArray(tokens);
 268  
     }
 269  
 
 270  
     /**
 271  
      * Copy the given Collection into a String array.
 272  
      * The Collection must contain String elements only.
 273  
      * <p/>
 274  
      * <p>Copied from the Spring Framework while retaining all license, copyright and author information.
 275  
      *
 276  
      * @param collection the Collection to copy
 277  
      * @return the String array (<code>null</code> if the passed-in
 278  
      *         Collection was <code>null</code>)
 279  
      */
 280  
     @SuppressWarnings({"unchecked"})
 281  
     public static String[] toStringArray(Collection collection) {
 282  0
         if (collection == null) {
 283  0
             return null;
 284  
         }
 285  0
         return (String[]) collection.toArray(new String[collection.size()]);
 286  
     }
 287  
 
 288  
     public static String[] splitKeyValue(String aLine) throws ParseException {
 289  26
         String line = clean(aLine);
 290  26
         if (line == null) {
 291  0
             return null;
 292  
         }
 293  26
         String[] split = line.split(" ", 2);
 294  26
         if (split.length != 2) {
 295  
             //fallback to checking for an equals sign
 296  0
             split = line.split("=", 2);
 297  0
             if (split.length != 2) {
 298  0
                 String msg = "Unable to determine Key/Value pair from line [" + line + "].  There is no space from " +
 299  
                         "which the split location could be determined.";
 300  0
                 throw new ParseException(msg, 0);
 301  
             }
 302  
 
 303  
         }
 304  
 
 305  26
         split[0] = clean(split[0]);
 306  26
         split[1] = clean(split[1]);
 307  26
         if (split[1].startsWith("=")) {
 308  
             //they used spaces followed by an equals followed by zero or more spaces to split the key/value pair, so
 309  
             //remove the equals sign to result in only the key and values in the
 310  26
             split[1] = clean(split[1].substring(1));
 311  
         }
 312  
 
 313  26
         if (split[0] == null) {
 314  0
             String msg = "No valid key could be found in line [" + line + "] to form a key/value pair.";
 315  0
             throw new ParseException(msg, 0);
 316  
         }
 317  26
         if (split[1] == null) {
 318  0
             String msg = "No corresponding value could be found in line [" + line + "] for key [" + split[0] + "]";
 319  0
             throw new ParseException(msg, 0);
 320  
         }
 321  
 
 322  26
         return split;
 323  
     }
 324  
 
 325  
     public static String[] split(String line) {
 326  68
         return split(line, DEFAULT_DELIMITER_CHAR);
 327  
     }
 328  
 
 329  
     public static String[] split(String line, char delimiter) {
 330  76
         return split(line, delimiter, DEFAULT_QUOTE_CHAR);
 331  
     }
 332  
 
 333  
     public static String[] split(String line, char delimiter, char quoteChar) {
 334  76
         return split(line, delimiter, quoteChar, quoteChar);
 335  
     }
 336  
 
 337  
     public static String[] split(String line, char delimiter, char beginQuoteChar, char endQuoteChar) {
 338  76
         return split(line, delimiter, beginQuoteChar, endQuoteChar, false, true);
 339  
     }
 340  
 
 341  
     /**
 342  
      * Splits the specified delimited String into tokens, supporting quoted tokens so that quoted strings themselves
 343  
      * won't be tokenized.
 344  
      * <p/>
 345  
      * This method's implementation is very loosely based (with significant modifications) on
 346  
      * <a href="http://blogs.bytecode.com.au/glen">Glen Smith</a>'s open-source
 347  
      * <a href="http://opencsv.svn.sourceforge.net/viewvc/opencsv/trunk/src/au/com/bytecode/opencsv/CSVReader.java?&view=markup">CSVReader.java</a>
 348  
      * file.
 349  
      * <p/>
 350  
      * That file is Apache 2.0 licensed as well, making Glen's code a great starting point for us to modify to
 351  
      * our needs.
 352  
      *
 353  
      * @param aLine          the String to parse
 354  
      * @param delimiter      the delimiter by which the <tt>line</tt> argument is to be split
 355  
      * @param beginQuoteChar the character signifying the start of quoted text (so the quoted text will not be split)
 356  
      * @param endQuoteChar   the character signifying the end of quoted text
 357  
      * @param retainQuotes   if the quotes themselves should be retained when constructing the corresponding token
 358  
      * @param trimTokens     if leading and trailing whitespace should be trimmed from discovered tokens.
 359  
      * @return the tokens discovered from parsing the given delimited <tt>line</tt>.
 360  
      */
 361  
     public static String[] split(String aLine, char delimiter, char beginQuoteChar, char endQuoteChar,
 362  
                                  boolean retainQuotes, boolean trimTokens) {
 363  80
         String line = clean(aLine);
 364  80
         if (line == null) {
 365  1
             return null;
 366  
         }
 367  
 
 368  79
         List<String> tokens = new ArrayList<String>();
 369  79
         StringBuilder sb = new StringBuilder();
 370  79
         boolean inQuotes = false;
 371  
 
 372  1611
         for (int i = 0; i < line.length(); i++) {
 373  
 
 374  1532
             char c = line.charAt(i);
 375  1532
             if (c == beginQuoteChar) {
 376  
                 // this gets complex... the quote may end a quoted block, or escape another quote.
 377  
                 // do a 1-char lookahead:
 378  15
                 if (inQuotes  // we are in quotes, therefore there can be escaped quotes in here.
 379  
                         && line.length() > (i + 1)  // there is indeed another character to check.
 380  
                         && line.charAt(i + 1) == beginQuoteChar) { // ..and that char. is a quote also.
 381  
                     // we have two quote chars in a row == one quote char, so consume them both and
 382  
                     // put one on the token. we do *not* exit the quoted text.
 383  2
                     sb.append(line.charAt(i + 1));
 384  2
                     i++;
 385  
                 } else {
 386  13
                     inQuotes = !inQuotes;
 387  13
                     if (retainQuotes) {
 388  3
                         sb.append(c);
 389  
                     }
 390  
                 }
 391  1517
             } else if (c == endQuoteChar) {
 392  6
                 inQuotes = !inQuotes;
 393  6
                 if (retainQuotes) {
 394  3
                     sb.append(c);
 395  
                 }
 396  1511
             } else if (c == delimiter && !inQuotes) {
 397  83
                 String s = sb.toString();
 398  83
                 if (trimTokens) {
 399  83
                     s = s.trim();
 400  
                 }
 401  83
                 tokens.add(s);
 402  83
                 sb = new StringBuilder(); // start work on next token
 403  83
             } else {
 404  1428
                 sb.append(c);
 405  
             }
 406  
         }
 407  79
         String s = sb.toString();
 408  79
         if (trimTokens) {
 409  79
             s = s.trim();
 410  
         }
 411  79
         tokens.add(s);
 412  79
         return tokens.toArray(new String[tokens.size()]);
 413  
     }
 414  
 
 415  
     /**
 416  
      * Joins the elements of the provided {@code Iterator} into
 417  
      * a single String containing the provided elements.</p>
 418  
      * <p/>
 419  
      * No delimiter is added before or after the list.
 420  
      * A {@code null} separator is the same as an empty String ("").</p>
 421  
      * <p/>
 422  
      * Copied from Commons Lang, version 3 (r1138702).</p>
 423  
      *
 424  
      * @param iterator  the {@code Iterator} of values to join together, may be null
 425  
      * @param separator the separator character to use, null treated as ""
 426  
      * @return the joined String, {@code null} if null iterator input
 427  
      * @since 1.2
 428  
      */
 429  
     public static String join(Iterator<?> iterator, String separator) {
 430  0
         final String empty = "";
 431  
 
 432  
         // handle null, zero and one elements before building a buffer
 433  0
         if (iterator == null) {
 434  0
             return null;
 435  
         }
 436  0
         if (!iterator.hasNext()) {
 437  0
             return empty;
 438  
         }
 439  0
         Object first = iterator.next();
 440  0
         if (!iterator.hasNext()) {
 441  0
             return first == null ? empty : first.toString();
 442  
         }
 443  
 
 444  
         // two or more elements
 445  0
         StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
 446  0
         if (first != null) {
 447  0
             buf.append(first);
 448  
         }
 449  
 
 450  0
         while (iterator.hasNext()) {
 451  0
             if (separator != null) {
 452  0
                 buf.append(separator);
 453  
             }
 454  0
             Object obj = iterator.next();
 455  0
             if (obj != null) {
 456  0
                 buf.append(obj);
 457  
             }
 458  0
         }
 459  0
         return buf.toString();
 460  
     }
 461  
 
 462  
     /**
 463  
      * Splits the {@code delimited} string (delimited by the specified {@code separator} character) and returns the
 464  
      * delimited values as a {@code Set}.
 465  
      * <p/>
 466  
      * If either argument is {@code null}, this method returns {@code null}.
 467  
      *
 468  
      * @param delimited the string to split
 469  
      * @param separator the character that delineates individual tokens to split
 470  
      * @return the delimited values as a {@code Set}.
 471  
      * @since 1.2
 472  
      */
 473  
     public static Set<String> splitToSet(String delimited, String separator) {
 474  6
         if (delimited == null || separator == null) {
 475  0
             return null;
 476  
         }
 477  6
         String[] split = split(delimited, separator.charAt(0));
 478  6
         return CollectionUtils.asSet(split);
 479  
     }
 480  
 
 481  
     /**
 482  
      * Returns the input argument, but ensures the first character is capitalized (if possible).
 483  
      * @param in the string to uppercase the first character.
 484  
      * @return the input argument, but with the first character capitalized (if possible).
 485  
      * @since 1.2
 486  
      */
 487  
     public static String uppercaseFirstChar(String in) {
 488  5
         if (in == null || in.length() == 0) {
 489  0
             return in;
 490  
         }
 491  5
         int length = in.length();
 492  5
         StringBuilder sb = new StringBuilder(length);
 493  
 
 494  5
         sb.append(Character.toUpperCase(in.charAt(0)));
 495  5
         if (length > 1) {
 496  5
             String remaining = in.substring(1);
 497  5
             sb.append(remaining);
 498  
         }
 499  5
         return sb.toString();
 500  
     }
 501  
 
 502  
 }