Coverage Report - org.apache.maven.shared.utils.StringUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
StringUtils
91%
496/544
85%
363/425
0
 
 1  
 package org.apache.maven.shared.utils;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 
 23  
 import java.util.Arrays;
 24  
 import java.util.Iterator;
 25  
 import java.util.Locale;
 26  
 import java.util.Map;
 27  
 import java.util.StringTokenizer;
 28  
 
 29  
 import javax.annotation.Nonnull;
 30  
 import javax.annotation.Nullable;
 31  
 
 32  
 /**
 33  
  * <p>Common <code>String</code> manipulation routines.</p>
 34  
  * <p/>
 35  
  * <p>Originally from
 36  
  * <a href="http://jakarta.apache.org/turbine/">Turbine</a>, the
 37  
  * GenerationJavaCore library and Velocity.
 38  
  * Later a lots methods from commons-lang StringUtils
 39  
  * got added too. Gradually smaller additions and fixes have been made
 40  
  * over the time by various ASF committers.</p>
 41  
  *
 42  
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 43  
  * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 44  
  * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
 45  
  * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
 46  
  * @author <a href="mailto:ed@apache.org">Ed Korthof</a>
 47  
  * @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a>
 48  
  * @author Stephen Colebourne
 49  
  * @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a>
 50  
  * @author Holger Krauth
 51  
  * @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
 52  
  * @version $Id: StringUtils.java 1402496 2012-10-26 13:00:39Z rfscholte $
 53  
  * 
 54  
  */
 55  
 @SuppressWarnings( "JavaDoc" )
 56  
 public class StringUtils
 57  
 {
 58  
     /**
 59  
      * <p><code>StringUtils</code> instances should NOT be constructed in
 60  
      * standard programming. Instead, the class should be used as
 61  
      * <code>StringUtils.trim(" foo ");</code>.</p>
 62  
      * <p/>
 63  
      * <p>This constructor is public to permit tools that require a JavaBean
 64  
      * manager to operate.</p>
 65  
      */
 66  
     public StringUtils()
 67  0
     {
 68  0
     }
 69  
 
 70  
     // Empty
 71  
     //--------------------------------------------------------------------------
 72  
 
 73  
     /**
 74  
      * <p>Removes control characters, including whitespace, from both
 75  
      * ends of this String, handling <code>null</code> by returning
 76  
      * an empty String.</p>
 77  
      *
 78  
      * @param str the String to check
 79  
      * @return the trimmed text (never <code>null</code>)
 80  
      * @see java.lang.String#trim()
 81  
      */
 82  
     public @Nonnull static String clean( String str )
 83  
     {
 84  4
         return ( str == null ? "" : str.trim() );
 85  
     }
 86  
 
 87  
     /**
 88  
      * <p>Removes control characters, including whitespace, from both
 89  
      * ends of this String, handling <code>null</code> by returning
 90  
      * <code>null</code>.</p>
 91  
      *
 92  
      * @param str the String to check
 93  
      * @return the trimmed text (or <code>null</code>)
 94  
      * @see java.lang.String#trim()
 95  
      */
 96  
     public static String trim( String str )
 97  
     {
 98  4
         return ( str == null ? null : str.trim() );
 99  
     }
 100  
 
 101  
     /**
 102  
      * <p>Deletes all whitespaces from a String.</p>
 103  
      * <p/>
 104  
      * <p>Whitespace is defined by
 105  
      * {@link Character#isWhitespace(char)}.</p>
 106  
      *
 107  
      * @param str String target to delete whitespace from
 108  
      * @return the String without whitespaces
 109  
      * @throws NullPointerException
 110  
      */
 111  
     public @Nonnull static String deleteWhitespace( @Nonnull String str )
 112  
     {
 113  5
         StringBuilder buffer = new StringBuilder();
 114  5
         int sz = str.length();
 115  32
         for ( int i = 0; i < sz; i++ )
 116  
         {
 117  28
             if ( !Character.isWhitespace( str.charAt( i ) ) )
 118  
             {
 119  11
                 buffer.append( str.charAt( i ) );
 120  
             }
 121  
         }
 122  4
         return buffer.toString();
 123  
     }
 124  
 
 125  
     /**
 126  
      * <p>Checks if a String is non <code>null</code> and is
 127  
      * not empty (<code>length > 0</code>).</p>
 128  
      *
 129  
      * @param str the String to check
 130  
      * @return true if the String is non-null, and not length zero
 131  
      */
 132  
     public static boolean isNotEmpty( String str )
 133  
     {
 134  7
         return ( ( str != null ) && ( str.length() > 0 ) );
 135  
     }
 136  
 
 137  
     /**
 138  
      * <p>Checks if a (trimmed) String is <code>null</code> or empty.</p>
 139  
      * <p/>
 140  
      * <p><strong>Note:</strong> In future releases, this method will no longer trim the input string such that it works
 141  
      * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be
 142  
      * migrated to use {@link #isBlank(String)} instead.</p>
 143  
      *
 144  
      * @param str the String to check
 145  
      * @return <code>true</code> if the String is <code>null</code>, or
 146  
      *         length zero once trimmed
 147  
      */
 148  
     public static boolean isEmpty( @Nullable String str )
 149  
     {
 150  25
         return ( ( str == null ) || ( str.trim().length() == 0 ) );
 151  
     }
 152  
 
 153  
     /**
 154  
      * <p>
 155  
      * Checks if a String is whitespace, empty ("") or null.
 156  
      * </p>
 157  
      * <p/>
 158  
      * <pre>
 159  
      * StringUtils.isBlank(null)      = true
 160  
      * StringUtils.isBlank("")        = true
 161  
      * StringUtils.isBlank(" ")       = true
 162  
      * StringUtils.isBlank("bob")     = false
 163  
      * StringUtils.isBlank("  bob  ") = false
 164  
      * </pre>
 165  
      *
 166  
      * @param str the String to check, may be null
 167  
      * @return <code>true</code> if the String is null, empty or whitespace
 168  
      * 
 169  
      */
 170  
     public static boolean isBlank( @Nullable String str )
 171  
     {
 172  
         int strLen;
 173  12
         if ( str == null || ( strLen = str.length() ) == 0 )
 174  
         {
 175  2
             return true;
 176  
         }
 177  30
         for ( int i = 0; i < strLen; i++ )
 178  
         {
 179  24
             if ( !Character.isWhitespace( str.charAt( i ) ) )
 180  
             {
 181  4
                 return false;
 182  
             }
 183  
         }
 184  6
         return true;
 185  
     }
 186  
 
 187  
     /**
 188  
      * <p>
 189  
      * Checks if a String is not empty (""), not null and not whitespace only.
 190  
      * </p>
 191  
      * <p/>
 192  
      * <pre>
 193  
      * StringUtils.isNotBlank(null)      = false
 194  
      * StringUtils.isNotBlank("")        = false
 195  
      * StringUtils.isNotBlank(" ")       = false
 196  
      * StringUtils.isNotBlank("bob")     = true
 197  
      * StringUtils.isNotBlank("  bob  ") = true
 198  
      * </pre>
 199  
      *
 200  
      * @param str the String to check, may be null
 201  
      * @return <code>true</code> if the String is not empty and not null and not whitespace
 202  
      * 
 203  
      */
 204  
     public static boolean isNotBlank( @Nullable String str )
 205  
     {
 206  6
         return !isBlank( str );
 207  
     }
 208  
 
 209  
     // Equals and IndexOf
 210  
     //--------------------------------------------------------------------------
 211  
 
 212  
     /**
 213  
      * <p>Compares two Strings, returning <code>true</code> if they are equal.</p>
 214  
      * <p/>
 215  
      * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
 216  
      * references are considered to be equal. The comparison is case sensitive.</p>
 217  
      *
 218  
      * @param str1 the first string
 219  
      * @param str2 the second string
 220  
      * @return <code>true</code> if the Strings are equal, case sensitive, or
 221  
      *         both <code>null</code>
 222  
      * @see java.lang.String#equals(Object)
 223  
      */
 224  
     public static boolean equals( @Nullable String str1, @Nullable String str2 )
 225  
     {
 226  5
         return ( str1 == null ? str2 == null : str1.equals( str2 ) );
 227  
     }
 228  
 
 229  
     /**
 230  
      * <p>Compares two Strings, returning <code>true</code> if they are equal ignoring
 231  
      * the case.</p>
 232  
      * <p/>
 233  
      * <p><code>Nulls</code> are handled without exceptions. Two <code>null</code>
 234  
      * references are considered equal. Comparison is case insensitive.</p>
 235  
      *
 236  
      * @param str1 the first string
 237  
      * @param str2 the second string
 238  
      * @return <code>true</code> if the Strings are equal, case insensitive, or
 239  
      *         both <code>null</code>
 240  
      * @see java.lang.String#equalsIgnoreCase(String)
 241  
      */
 242  
     public static boolean equalsIgnoreCase( String str1, String str2 )
 243  
     {
 244  6
         return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) );
 245  
     }
 246  
 
 247  
     /**
 248  
      * <p>Find the first index of any of a set of potential substrings.</p>
 249  
      * <p/>
 250  
      * <p><code>null</code> String will return <code>-1</code>.</p>
 251  
      *
 252  
      * @param str        the String to check
 253  
      * @param searchStrs the Strings to search for
 254  
      * @return the first index of any of the searchStrs in str
 255  
      * @throws NullPointerException if any of searchStrs[i] is <code>null</code>
 256  
      */
 257  
     public static int indexOfAny( String str, String... searchStrs )
 258  
     {
 259  4
         if ( ( str == null ) || ( searchStrs == null ) )
 260  
         {
 261  3
             return -1;
 262  
         }
 263  
         // String's can't have a MAX_VALUEth index.
 264  1
         int ret = Integer.MAX_VALUE;
 265  
 
 266  
         int tmp;
 267  3
         for ( String searchStr : searchStrs )
 268  
         {
 269  2
             tmp = str.indexOf( searchStr );
 270  2
             if ( tmp == -1 )
 271  
             {
 272  1
                 continue;
 273  
             }
 274  
 
 275  1
             if ( tmp < ret )
 276  
             {
 277  1
                 ret = tmp;
 278  
             }
 279  
         }
 280  
 
 281  1
         return ( ret == Integer.MAX_VALUE ) ? -1 : ret;
 282  
     }
 283  
 
 284  
     /**
 285  
      * <p>Find the latest index of any of a set of potential substrings.</p>
 286  
      * <p/>
 287  
      * <p><code>null</code> string will return <code>-1</code>.</p>
 288  
      *
 289  
      * @param str        the String to check
 290  
      * @param searchStrs the Strings to search for
 291  
      * @return the last index of any of the Strings
 292  
      * @throws NullPointerException if any of searchStrs[i] is <code>null</code>
 293  
      */
 294  
     public static int lastIndexOfAny( String str, String... searchStrs )
 295  
     {
 296  4
         if ( ( str == null ) || ( searchStrs == null ) )
 297  
         {
 298  2
             return -1;
 299  
         }
 300  2
         int ret = -1;
 301  
         int tmp;
 302  6
         for ( String searchStr : searchStrs )
 303  
         {
 304  4
             tmp = str.lastIndexOf( searchStr );
 305  4
             if ( tmp > ret )
 306  
             {
 307  1
                 ret = tmp;
 308  
             }
 309  
         }
 310  2
         return ret;
 311  
     }
 312  
 
 313  
     // Substring
 314  
     //--------------------------------------------------------------------------
 315  
 
 316  
     /**
 317  
      * <p>Gets a substring from the specified string avoiding exceptions.</p>
 318  
      * <p/>
 319  
      * <p>A negative start position can be used to start <code>n</code>
 320  
      * characters from the end of the String.</p>
 321  
      *
 322  
      * @param str   the String to get the substring from
 323  
      * @param start the position to start from, negative means
 324  
      *              count back from the end of the String by this many characters
 325  
      * @return substring from start position
 326  
      */
 327  
     public static String substring( String str, int start )
 328  
     {
 329  5
         if ( str == null )
 330  
         {
 331  2
             return null;
 332  
         }
 333  
 
 334  
         // handle negatives, which means last n characters
 335  3
         if ( start < 0 )
 336  
         {
 337  1
             start = str.length() + start; // remember start is negative
 338  
         }
 339  
 
 340  3
         if ( start < 0 )
 341  
         {
 342  0
             start = 0;
 343  
         }
 344  3
         if ( start > str.length() )
 345  
         {
 346  1
             return "";
 347  
         }
 348  
 
 349  2
         return str.substring( start );
 350  
     }
 351  
 
 352  
     /**
 353  
      * <p>Gets a substring from the specified String avoiding exceptions.</p>
 354  
      * <p/>
 355  
      * <p>A negative start position can be used to start/end <code>n</code>
 356  
      * characters from the end of the String.</p>
 357  
      *
 358  
      * @param str   the String to get the substring from
 359  
      * @param start the position to start from, negative means
 360  
      *              count back from the end of the string by this many characters
 361  
      * @param end   the position to end at (exclusive), negative means
 362  
      *              count back from the end of the String by this many characters
 363  
      * @return substring from start position to end positon
 364  
      */
 365  
     public static String substring( String str, int start, int end )
 366  
     {
 367  6
         if ( str == null )
 368  
         {
 369  2
             return null;
 370  
         }
 371  
 
 372  
         // handle negatives
 373  4
         if ( end < 0 )
 374  
         {
 375  0
             end = str.length() + end; // remember end is negative
 376  
         }
 377  4
         if ( start < 0 )
 378  
         {
 379  1
             start = str.length() + start; // remember start is negative
 380  
         }
 381  
 
 382  
         // check length next
 383  4
         if ( end > str.length() )
 384  
         {
 385  
             // check this works.
 386  1
             end = str.length();
 387  
         }
 388  
 
 389  
         // if start is greater than end, return ""
 390  4
         if ( start > end )
 391  
         {
 392  2
             return "";
 393  
         }
 394  
 
 395  2
         if ( start < 0 )
 396  
         {
 397  0
             start = 0;
 398  
         }
 399  2
         if ( end < 0 )
 400  
         {
 401  0
             end = 0;
 402  
         }
 403  
 
 404  2
         return str.substring( start, end );
 405  
     }
 406  
 
 407  
     /**
 408  
      * <p>Gets the leftmost <code>n</code> characters of a String.</p>
 409  
      * <p/>
 410  
      * <p>If <code>n</code> characters are not available, or the
 411  
      * String is <code>null</code>, the String will be returned without
 412  
      * an exception.</p>
 413  
      *
 414  
      * @param str the String to get the leftmost characters from
 415  
      * @param len the length of the required String
 416  
      * @return the leftmost characters
 417  
      * @throws IllegalArgumentException if len is less than zero
 418  
      */
 419  
     public static String left( String str, int len )
 420  
     {
 421  5
         if ( len < 0 )
 422  
         {
 423  1
             throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
 424  
         }
 425  4
         if ( ( str == null ) || ( str.length() <= len ) )
 426  
         {
 427  2
             return str;
 428  
         }
 429  
         else
 430  
         {
 431  2
             return str.substring( 0, len );
 432  
         }
 433  
     }
 434  
 
 435  
     /**
 436  
      * <p>Gets the rightmost <code>n</code> characters of a String.</p>
 437  
      * <p/>
 438  
      * <p>If <code>n</code> characters are not available, or the String
 439  
      * is <code>null</code>, the String will be returned without an
 440  
      * exception.</p>
 441  
      *
 442  
      * @param str the String to get the rightmost characters from
 443  
      * @param len the length of the required String
 444  
      * @return the leftmost characters
 445  
      * @throws IllegalArgumentException if len is less than zero
 446  
      */
 447  
     public static String right( String str, int len )
 448  
     {
 449  6
         if ( len < 0 )
 450  
         {
 451  2
             throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
 452  
         }
 453  4
         if ( ( str == null ) || ( str.length() <= len ) )
 454  
         {
 455  1
             return str;
 456  
         }
 457  
         else
 458  
         {
 459  3
             return str.substring( str.length() - len );
 460  
         }
 461  
     }
 462  
 
 463  
     /**
 464  
      * <p>Gets <code>n</code> characters from the middle of a String.</p>
 465  
      * <p/>
 466  
      * <p>If <code>n</code> characters are not available, the remainder
 467  
      * of the String will be returned without an exception. If the
 468  
      * String is <code>null</code>, <code>null</code> will be returned.</p>
 469  
      *
 470  
      * @param str the String to get the characters from
 471  
      * @param pos the position to start from
 472  
      * @param len the length of the required String
 473  
      * @return the leftmost characters
 474  
      * @throws IndexOutOfBoundsException if pos is out of bounds
 475  
      * @throws IllegalArgumentException  if len is less than zero
 476  
      */
 477  
     public static String mid( String str, int pos, int len )
 478  
     {
 479  5
         if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) )
 480  
         {
 481  1
             throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" );
 482  
         }
 483  4
         if ( len < 0 )
 484  
         {
 485  1
             throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
 486  
         }
 487  3
         if ( str == null )
 488  
         {
 489  1
             return null;
 490  
         }
 491  2
         if ( str.length() <= ( pos + len ) )
 492  
         {
 493  0
             return str.substring( pos );
 494  
         }
 495  
         else
 496  
         {
 497  2
             return str.substring( pos, pos + len );
 498  
         }
 499  
     }
 500  
 
 501  
     // Splitting
 502  
     //--------------------------------------------------------------------------
 503  
 
 504  
     /**
 505  
      * <p>Splits the provided text into a array, using whitespace as the
 506  
      * separator.</p>
 507  
      * <p/>
 508  
      * <p>The separator is not included in the returned String array.</p>
 509  
      *
 510  
      * @param str the String to parse
 511  
      * @return an array of parsed Strings
 512  
      */
 513  
     public @Nonnull static String[] split( @Nonnull String str )
 514  
     {
 515  3
         return split( str, null, -1 );
 516  
     }
 517  
 
 518  
     /**
 519  
      * @see #split(String, String, int)
 520  
      */
 521  
     public @Nonnull static String[] split( @Nonnull String text, String separator )
 522  
     {
 523  50
         return split( text, separator, -1 );
 524  
     }
 525  
 
 526  
     /**
 527  
      * <p>Splits the provided text into a array, based on a given separator.</p>
 528  
      * <p/>
 529  
      * <p>The separator is not included in the returned String array. The
 530  
      * maximum number of splits to perfom can be controlled. A <code>null</code>
 531  
      * separator will cause parsing to be on whitespace.</p>
 532  
      * <p/>
 533  
      * <p>This is useful for quickly splitting a String directly into
 534  
      * an array of tokens, instead of an enumeration of tokens (as
 535  
      * <code>StringTokenizer</code> does).</p>
 536  
      *
 537  
      * @param str       The string to parse.
 538  
      * @param separator Characters used as the delimiters. If
 539  
      *                  <code>null</code>, splits on whitespace.
 540  
      * @param max       The maximum number of elements to include in the
 541  
      *                  array.  A zero or negative value implies no limit.
 542  
      * @return an array of parsed Strings
 543  
      */
 544  
     public @Nonnull static String[] split( @Nonnull String str, String separator, int max )
 545  
     {
 546  
         StringTokenizer tok;
 547  62
         if ( separator == null )
 548  
         {
 549  
             // Null separator means we're using StringTokenizer's default
 550  
             // delimiter, which comprises all whitespace characters.
 551  11
             tok = new StringTokenizer( str );
 552  
         }
 553  
         else
 554  
         {
 555  51
             tok = new StringTokenizer( str, separator );
 556  
         }
 557  
 
 558  55
         int listSize = tok.countTokens();
 559  55
         if ( ( max > 0 ) && ( listSize > max ) )
 560  
         {
 561  1
             listSize = max;
 562  
         }
 563  
 
 564  55
         String[] list = new String[listSize];
 565  55
         int i = 0;
 566  
         int lastTokenBegin;
 567  55
         int lastTokenEnd = 0;
 568  160
         while ( tok.hasMoreTokens() )
 569  
         {
 570  110
             if ( ( max > 0 ) && ( i == listSize - 1 ) )
 571  
             {
 572  
                 // In the situation where we hit the max yet have
 573  
                 // tokens left over in our input, the last list
 574  
                 // element gets all remaining text.
 575  5
                 String endToken = tok.nextToken();
 576  5
                 lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
 577  5
                 list[i] = str.substring( lastTokenBegin );
 578  5
                 break;
 579  
             }
 580  
             else
 581  
             {
 582  105
                 list[i] = tok.nextToken();
 583  105
                 lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
 584  105
                 lastTokenEnd = lastTokenBegin + list[i].length();
 585  
             }
 586  105
             i++;
 587  
         }
 588  55
         return list;
 589  
     }
 590  
 
 591  
     // Joining
 592  
     //--------------------------------------------------------------------------
 593  
 
 594  
     /**
 595  
      * <p>Concatenates elements of an array into a single String.</p>
 596  
      * <p/>
 597  
      * <p>The difference from join is that concatenate has no delimiter.</p>
 598  
      *
 599  
      * @param array the array of values to concatenate.
 600  
      * @return the concatenated string.
 601  
      */
 602  
     public @Nonnull static String concatenate( @Nonnull Object... array )
 603  
     {
 604  4
         return join( array, "" );
 605  
     }
 606  
 
 607  
     /**
 608  
      * <p>Joins the elements of the provided array into a single String
 609  
      * containing the provided list of elements.</p>
 610  
      * <p/>
 611  
      * <p>No delimiter is added before or after the list. A
 612  
      * <code>null</code> separator is the same as a blank String.</p>
 613  
      *
 614  
      * @param array     the array of values to join together
 615  
      * @param separator the separator character to use
 616  
      * @return the joined String
 617  
      */
 618  
     public @Nonnull static String join( @Nonnull Object[] array, String separator )
 619  
     {
 620  14
         if ( separator == null )
 621  
         {
 622  4
             separator = "";
 623  
         }
 624  14
         int arraySize = array.length;
 625  12
         int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize );
 626  12
         StringBuilder buf = new StringBuilder( bufSize );
 627  
 
 628  64
         for ( int i = 0; i < arraySize; i++ )
 629  
         {
 630  52
             if ( i > 0 )
 631  
             {
 632  43
                 buf.append( separator );
 633  
             }
 634  52
             buf.append( array[i] );
 635  
         }
 636  12
         return buf.toString();
 637  
     }
 638  
 
 639  
     /**
 640  
      * <p>Joins the elements of the provided <code>Iterator</code> into
 641  
      * a single String containing the provided elements.</p>
 642  
      * <p/>
 643  
      * <p>No delimiter is added before or after the list. A
 644  
      * <code>null</code> separator is the same as a blank String.</p>
 645  
      *
 646  
      * @param iterator  the <code>Iterator</code> of values to join together
 647  
      * @param separator the separator character to use
 648  
      * @return the joined String
 649  
      */
 650  
     public @Nonnull static String join( @Nonnull Iterator<?> iterator, String separator )
 651  
     {
 652  10
         if ( separator == null )
 653  
         {
 654  3
             separator = "";
 655  
         }
 656  10
         StringBuilder buf = new StringBuilder( 256 );  // Java default is 16, probably too small
 657  34
         while ( iterator.hasNext() )
 658  
         {
 659  24
             buf.append( iterator.next() );
 660  24
             if ( iterator.hasNext() )
 661  
             {
 662  16
                 buf.append( separator );
 663  
             }
 664  
         }
 665  9
         return buf.toString();
 666  
     }
 667  
 
 668  
     // Replacing
 669  
     //--------------------------------------------------------------------------
 670  
 
 671  
     /**
 672  
      * <p>Replace a char with another char inside a larger String, once.</p>
 673  
      * <p/>
 674  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 675  
      *
 676  
      * @param text text to search and replace in
 677  
      * @param repl char to search for
 678  
      * @param with char to replace with
 679  
      * @return the text with any replacements processed
 680  
      * @see #replace(String text, char repl, char with, int max)
 681  
      */
 682  
     public static String replaceOnce( @Nullable String text, char repl, char with )
 683  
     {
 684  3
         return replace( text, repl, with, 1 );
 685  
     }
 686  
 
 687  
     /**
 688  
      * <p>Replace all occurances of a char within another char.</p>
 689  
      * <p/>
 690  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 691  
      *
 692  
      * @param text text to search and replace in
 693  
      * @param repl char to search for
 694  
      * @param with char to replace with
 695  
      * @return the text with any replacements processed
 696  
      * @see #replace(String text, char repl, char with, int max)
 697  
      */
 698  
     public static String replace( @Nullable String text, char repl, char with )
 699  
     {
 700  4
         return replace( text, repl, with, -1 );
 701  
     }
 702  
 
 703  
     /**
 704  
      * <p>Replace a char with another char inside a larger String,
 705  
      * for the first <code>max</code> values of the search char.</p>
 706  
      * <p/>
 707  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 708  
      *
 709  
      * @param text text to search and replace in
 710  
      * @param repl char to search for
 711  
      * @param with char to replace with
 712  
      * @param max  maximum number of values to replace, or <code>-1</code> if no maximum
 713  
      * @return the text with any replacements processed
 714  
      */
 715  
     public static String replace( @Nullable String text, char repl, char with, int max )
 716  
     {
 717  13
         return replace( text, String.valueOf( repl ), String.valueOf( with ), max );
 718  
     }
 719  
 
 720  
     /**
 721  
      * <p>Replace a String with another String inside a larger String, once.</p>
 722  
      * <p/>
 723  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 724  
      *
 725  
      * @param text text to search and replace in
 726  
      * @param repl String to search for
 727  
      * @param with String to replace with
 728  
      * @return the text with any replacements processed
 729  
      * @see #replace(String text, String repl, String with, int max)
 730  
      */
 731  
     public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with )
 732  
     {
 733  3
         return replace( text, repl, with, 1 );
 734  
     }
 735  
 
 736  
     /**
 737  
      * <p>Replace all occurances of a String within another String.</p>
 738  
      * <p/>
 739  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 740  
      *
 741  
      * @param text text to search and replace in
 742  
      * @param repl String to search for
 743  
      * @param with String to replace with
 744  
      * @return the text with any replacements processed
 745  
      * @see #replace(String text, String repl, String with, int max)
 746  
      */
 747  
     public static String replace( @Nullable String text, @Nullable  String repl, @Nullable String with )
 748  
     {
 749  6
         return replace( text, repl, with, -1 );
 750  
     }
 751  
 
 752  
     /**
 753  
      * <p>Replace a String with another String inside a larger String,
 754  
      * for the first <code>max</code> values of the search String.</p>
 755  
      * <p/>
 756  
      * <p>A <code>null</code> reference passed to this method is a no-op.</p>
 757  
      *
 758  
      * @param text text to search and replace in
 759  
      * @param repl String to search for
 760  
      * @param with String to replace with
 761  
      * @param max  maximum number of values to replace, or <code>-1</code> if no maximum
 762  
      * @return the text with any replacements processed
 763  
      */
 764  
     public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max )
 765  
     {
 766  29
         if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) )
 767  
         {
 768  6
             return text;
 769  
         }
 770  
 
 771  23
         StringBuilder buf = new StringBuilder( text.length() );
 772  23
         int start = 0, end;
 773  46
         while ( ( end = text.indexOf( repl, start ) ) != -1 )
 774  
         {
 775  29
             buf.append( text.substring( start, end ) ).append( with );
 776  29
             start = end + repl.length();
 777  
 
 778  29
             if ( --max == 0 )
 779  
             {
 780  6
                 break;
 781  
             }
 782  
         }
 783  23
         buf.append( text.substring( start ) );
 784  23
         return buf.toString();
 785  
     }
 786  
 
 787  
     /**
 788  
      * <p>Overlay a part of a String with another String.</p>
 789  
      *
 790  
      * @param text    String to do overlaying in
 791  
      * @param overlay String to overlay
 792  
      * @param start   int to start overlaying at
 793  
      * @param end     int to stop overlaying before
 794  
      * @return String with overlayed text
 795  
      * @throws NullPointerException if text or overlay is <code>null</code>
 796  
      */
 797  
     @SuppressWarnings( "ConstantConditions" )
 798  
     public @Nonnull static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end )
 799  
     {
 800  5
         if ( overlay == null )
 801  
         {
 802  2
             throw new NullPointerException( "overlay is null" );
 803  
         }
 804  3
         return text.substring( 0, start ) + overlay + text.substring( end );
 805  
     }
 806  
 
 807  
     // Centering
 808  
     //--------------------------------------------------------------------------
 809  
 
 810  
     /**
 811  
      * <p>Center a String in a larger String of size <code>n</code>.<p>
 812  
      * <p/>
 813  
      * <p>Uses spaces as the value to buffer the String with.
 814  
      * Equivalent to <code>center(str, size, " ")</code>.</p>
 815  
      *
 816  
      * @param str  String to center
 817  
      * @param size int size of new String
 818  
      * @return String containing centered String
 819  
      * @throws NullPointerException if str is <code>null</code>
 820  
      */
 821  
     public @Nonnull static String center( @Nonnull String str, int size )
 822  
     {
 823  4
         return center( str, size, " " );
 824  
     }
 825  
 
 826  
     /**
 827  
      * <p>Center a String in a larger String of size <code>n</code>.</p>
 828  
      * <p/>
 829  
      * <p>Uses a supplied String as the value to buffer the String with.</p>
 830  
      *
 831  
      * @param str   String to center
 832  
      * @param size  int size of new String
 833  
      * @param delim String to buffer the new String with
 834  
      * @return String containing centered String
 835  
      * @throws NullPointerException if str or delim is <code>null</code>
 836  
      * @throws ArithmeticException  if delim is the empty String
 837  
      */
 838  
     public @Nonnull static String center( @Nonnull String str, int size, @Nonnull String delim )
 839  
     {
 840  8
         int sz = str.length();
 841  6
         int p = size - sz;
 842  6
         if ( p < 1 )
 843  
         {
 844  2
             return str;
 845  
         }
 846  4
         str = leftPad( str, sz + p / 2, delim );
 847  4
         str = rightPad( str, size, delim );
 848  4
         return str;
 849  
     }
 850  
 
 851  
     // Chomping
 852  
     //--------------------------------------------------------------------------
 853  
 
 854  
     /**
 855  
      * <p>Remove the last newline, and everything after it from a String.</p>
 856  
      *
 857  
      * @param str String to chomp the newline from
 858  
      * @return String without chomped newline
 859  
      * @throws NullPointerException if str is <code>null</code>
 860  
      */
 861  
     public @Nonnull static String chomp( @Nonnull String str )
 862  
     {
 863  5
         return chomp( str, "\n" );
 864  
     }
 865  
 
 866  
     /**
 867  
      * <p>Remove the last value of a supplied String, and everything after
 868  
      * it from a String.</p>
 869  
      *
 870  
      * @param str String to chomp from
 871  
      * @param sep String to chomp
 872  
      * @return String without chomped ending
 873  
      * @throws NullPointerException if str or sep is <code>null</code>
 874  
      */
 875  
     public @Nonnull static String chomp( @Nonnull String str, @Nonnull String sep )
 876  
     {
 877  10
         int idx = str.lastIndexOf( sep );
 878  8
         if ( idx != -1 )
 879  
         {
 880  6
             return str.substring( 0, idx );
 881  
         }
 882  
         else
 883  
         {
 884  2
             return str;
 885  
         }
 886  
     }
 887  
 
 888  
     /**
 889  
      * <p>Remove a newline if and only if it is at the end
 890  
      * of the supplied String.</p>
 891  
      *
 892  
      * @param str String to chomp from
 893  
      * @return String without chomped ending
 894  
      * @throws NullPointerException if str is <code>null</code>
 895  
      */
 896  
     public @Nonnull static String chompLast( @Nonnull String str )
 897  
     {
 898  6
         return chompLast( str, "\n" );
 899  
     }
 900  
 
 901  
     /**
 902  
      * <p>Remove a value if and only if the String ends with that value.</p>
 903  
      *
 904  
      * @param str String to chomp from
 905  
      * @param sep String to chomp
 906  
      * @return String without chomped ending
 907  
      * @throws NullPointerException if str or sep is <code>null</code>
 908  
      */
 909  
     public @Nonnull static String chompLast( @Nonnull String str, @Nonnull String sep )
 910  
     {
 911  16
         if ( str.length() == 0 )
 912  
         {
 913  0
             return str;
 914  
         }
 915  14
         String sub = str.substring( str.length() - sep.length() );
 916  14
         if ( sep.equals( sub ) )
 917  
         {
 918  8
             return str.substring( 0, str.length() - sep.length() );
 919  
         }
 920  
         else
 921  
         {
 922  6
             return str;
 923  
         }
 924  
     }
 925  
 
 926  
     /**
 927  
      * <p>Remove everything and return the last value of a supplied String, and
 928  
      * everything after it from a String.</p>
 929  
      *
 930  
      * @param str String to chomp from
 931  
      * @param sep String to chomp
 932  
      * @return String chomped
 933  
      * @throws NullPointerException if str or sep is <code>null</code>
 934  
      */
 935  
     public @Nonnull static String getChomp( @Nonnull String str, @Nonnull String sep )
 936  
     {
 937  6
         int idx = str.lastIndexOf( sep );
 938  3
         if ( idx == str.length() - sep.length() )
 939  
         {
 940  1
             return sep;
 941  
         }
 942  2
         else if ( idx != -1 )
 943  
         {
 944  1
             return str.substring( idx );
 945  
         }
 946  
         else
 947  
         {
 948  1
             return "";
 949  
         }
 950  
     }
 951  
 
 952  
     /**
 953  
      * <p>Remove the first value of a supplied String, and everything before it
 954  
      * from a String.</p>
 955  
      *
 956  
      * @param str String to chomp from
 957  
      * @param sep String to chomp
 958  
      * @return String without chomped beginning
 959  
      * @throws NullPointerException if str or sep is <code>null</code>
 960  
      */
 961  
     public @Nonnull static String prechomp( @Nonnull String str, @Nonnull String sep )
 962  
     {
 963  5
         int idx = str.indexOf( sep );
 964  2
         if ( idx != -1 )
 965  
         {
 966  1
             return str.substring( idx + sep.length() );
 967  
         }
 968  
         else
 969  
         {
 970  1
             return str;
 971  
         }
 972  
     }
 973  
 
 974  
     /**
 975  
      * <p>Remove and return everything before the first value of a
 976  
      * supplied String from another String.</p>
 977  
      *
 978  
      * @param str String to chomp from
 979  
      * @param sep String to chomp
 980  
      * @return String prechomped
 981  
      * @throws NullPointerException if str or sep is <code>null</code>
 982  
      */
 983  
     public @Nonnull static String getPrechomp( @Nonnull String str, String sep )
 984  
     {
 985  4
         int idx = str.indexOf( sep );
 986  2
         if ( idx != -1 )
 987  
         {
 988  1
             return str.substring( 0, idx + sep.length() );
 989  
         }
 990  
         else
 991  
         {
 992  1
             return "";
 993  
         }
 994  
     }
 995  
 
 996  
     // Chopping
 997  
     //--------------------------------------------------------------------------
 998  
 
 999  
     /**
 1000  
      * <p>Remove the last character from a String.</p>
 1001  
      * <p/>
 1002  
      * <p>If the String ends in <code>\r\n</code>, then remove both
 1003  
      * of them.</p>
 1004  
      *
 1005  
      * @param str String to chop last character from
 1006  
      * @return String without last character
 1007  
      * @throws NullPointerException if str is <code>null</code>
 1008  
      */
 1009  
     public @Nonnull static String chop( @Nonnull String str )
 1010  
     {
 1011  8
         if ( "".equals( str ) )
 1012  
         {
 1013  0
             return "";
 1014  
         }
 1015  8
         if ( str.length() == 1 )
 1016  
         {
 1017  1
             return "";
 1018  
         }
 1019  6
         int lastIdx = str.length() - 1;
 1020  6
         String ret = str.substring( 0, lastIdx );
 1021  6
         char last = str.charAt( lastIdx );
 1022  6
         if ( last == '\n' )
 1023  
         {
 1024  2
             if ( ret.charAt( lastIdx - 1 ) == '\r' )
 1025  
             {
 1026  1
                 return ret.substring( 0, lastIdx - 1 );
 1027  
             }
 1028  
         }
 1029  5
         return ret;
 1030  
     }
 1031  
 
 1032  
     /**
 1033  
      * <p>Remove <code>\n</code> from end of a String if it's there.
 1034  
      * If a <code>\r</code> precedes it, then remove that too.</p>
 1035  
      *
 1036  
      * @param str String to chop a newline from
 1037  
      * @return String without newline
 1038  
      * @throws NullPointerException if str is <code>null</code>
 1039  
      */
 1040  
     public @Nonnull static String chopNewline( @Nonnull String str )
 1041  
     {
 1042  6
         int lastIdx = str.length() - 1;
 1043  5
         char last = str.charAt( lastIdx );
 1044  5
         if ( last == '\n' )
 1045  
         {
 1046  2
             if ( str.charAt( lastIdx - 1 ) == '\r' )
 1047  
             {
 1048  1
                 lastIdx--;
 1049  
             }
 1050  
         }
 1051  
         else
 1052  
         {
 1053  3
             lastIdx++;
 1054  
         }
 1055  5
         return str.substring( 0, lastIdx );
 1056  
     }
 1057  
 
 1058  
     // Conversion
 1059  
     //--------------------------------------------------------------------------
 1060  
 
 1061  
     // spec 3.10.6
 1062  
 
 1063  
     /**
 1064  
      * <p>Escapes any values it finds into their String form.</p>
 1065  
      * <p/>
 1066  
      * <p>So a tab becomes the characters <code>'\\'</code> and
 1067  
      * <code>'t'</code>.</p>
 1068  
      *
 1069  
      * @param str String to escape values in
 1070  
      * @return String with escaped values
 1071  
      * @throws NullPointerException if str is <code>null</code>
 1072  
      */
 1073  
     public @Nonnull static String escape( @Nonnull String str )
 1074  
     {
 1075  
         // improved with code from  cybertiger@cyberiantiger.org
 1076  
         // unicode from him, and defaul for < 32's.
 1077  4
         int sz = str.length();
 1078  3
         StringBuilder buffer = new StringBuilder( 2 * sz );
 1079  28
         for ( int i = 0; i < sz; i++ )
 1080  
         {
 1081  25
             char ch = str.charAt( i );
 1082  
 
 1083  
             // handle unicode
 1084  25
             if ( ch > 0xfff )
 1085  
             {
 1086  0
                 buffer.append( "\\u" ).append( Integer.toHexString( ch ) );
 1087  
             }
 1088  25
             else if ( ch > 0xff )
 1089  
             {
 1090  0
                 buffer.append( "\\u0" ).append( Integer.toHexString( ch ) );
 1091  
             }
 1092  25
             else if ( ch > 0x7f )
 1093  
             {
 1094  0
                 buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
 1095  
             }
 1096  25
             else if ( ch < 32 )
 1097  
             {
 1098  2
                 switch ( ch )
 1099  
                 {
 1100  
                     case '\b':
 1101  0
                         buffer.append( '\\' );
 1102  0
                         buffer.append( 'b' );
 1103  0
                         break;
 1104  
                     case '\n':
 1105  1
                         buffer.append( '\\' );
 1106  1
                         buffer.append( 'n' );
 1107  1
                         break;
 1108  
                     case '\t':
 1109  1
                         buffer.append( '\\' );
 1110  1
                         buffer.append( 't' );
 1111  1
                         break;
 1112  
                     case '\f':
 1113  0
                         buffer.append( '\\' );
 1114  0
                         buffer.append( 'f' );
 1115  0
                         break;
 1116  
                     case '\r':
 1117  0
                         buffer.append( '\\' );
 1118  0
                         buffer.append( 'r' );
 1119  0
                         break;
 1120  
                     default:
 1121  0
                         if ( ch > 0xf )
 1122  
                         {
 1123  0
                             buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
 1124  
                         }
 1125  
                         else
 1126  
                         {
 1127  0
                             buffer.append( "\\u000" ).append( Integer.toHexString( ch ) );
 1128  
                         }
 1129  0
                         break;
 1130  
                 }
 1131  
             }
 1132  
             else
 1133  
             {
 1134  23
                 switch ( ch )
 1135  
                 {
 1136  
                     case '\'':
 1137  0
                         buffer.append( '\\' );
 1138  0
                         buffer.append( '\'' );
 1139  0
                         break;
 1140  
                     case '"':
 1141  0
                         buffer.append( '\\' );
 1142  0
                         buffer.append( '"' );
 1143  0
                         break;
 1144  
                     case '\\':
 1145  0
                         buffer.append( '\\' );
 1146  0
                         buffer.append( '\\' );
 1147  0
                         break;
 1148  
                     default:
 1149  23
                         buffer.append( ch );
 1150  
                         break;
 1151  
                 }
 1152  
             }
 1153  
         }
 1154  3
         return buffer.toString();
 1155  
     }
 1156  
 
 1157  
     // Padding
 1158  
     //--------------------------------------------------------------------------
 1159  
 
 1160  
     /**
 1161  
      * <p>Repeat a String <code>n</code> times to form a
 1162  
      * new string.</p>
 1163  
      *
 1164  
      * @param str    String to repeat
 1165  
      * @param repeat number of times to repeat str
 1166  
      * @return String with repeated String
 1167  
      * @throws NegativeArraySizeException if <code>repeat < 0</code>
 1168  
      * @throws NullPointerException       if str is <code>null</code>
 1169  
      */
 1170  
     public @Nonnull static String repeat( @Nonnull String str, int repeat )
 1171  
     {
 1172  136
         StringBuilder buffer = new StringBuilder( repeat * str.length() );
 1173  2155
         for ( int i = 0; i < repeat; i++ )
 1174  
         {
 1175  2021
             buffer.append( str );
 1176  
         }
 1177  134
         return buffer.toString();
 1178  
     }
 1179  
 
 1180  
     /**
 1181  
      * <p>Right pad a String with spaces.</p>
 1182  
      * <p/>
 1183  
      * <p>The String is padded to the size of <code>n</code>.</p>
 1184  
      *
 1185  
      * @param str  String to repeat
 1186  
      * @param size number of times to repeat str
 1187  
      * @return right padded String
 1188  
      * @throws NullPointerException if str is <code>null</code>
 1189  
      */
 1190  
     public @Nonnull static String rightPad( @Nonnull String str, int size )
 1191  
     {
 1192  4
         return rightPad( str, size, " " );
 1193  
     }
 1194  
 
 1195  
     /**
 1196  
      * <p>Right pad a String with a specified string.</p>
 1197  
      * <p/>
 1198  
      * <p>The String is padded to the size of <code>n</code>.</p>
 1199  
      *
 1200  
      * @param str   String to pad out
 1201  
      * @param size  size to pad to
 1202  
      * @param delim String to pad with
 1203  
      * @return right padded String
 1204  
      * @throws NullPointerException if str or delim is <code>null</code>
 1205  
      * @throws ArithmeticException  if delim is the empty String
 1206  
      */
 1207  
     public @Nonnull static String rightPad( @Nonnull String str, int size, @Nonnull String delim )
 1208  
     {
 1209  14
         size = ( size - str.length() ) / delim.length();
 1210  10
         if ( size > 0 )
 1211  
         {
 1212  6
             str += repeat( delim, size );
 1213  
         }
 1214  10
         return str;
 1215  
     }
 1216  
 
 1217  
     /**
 1218  
      * <p>Left pad a String with spaces.</p>
 1219  
      * <p/>
 1220  
      * <p>The String is padded to the size of <code>n</code>.</p>
 1221  
      *
 1222  
      * @param str  String to pad out
 1223  
      * @param size size to pad to
 1224  
      * @return left padded String
 1225  
      * @throws NullPointerException if str or delim is <code>null</code>
 1226  
      */
 1227  
     public @Nonnull static String leftPad( @Nonnull String str, int size )
 1228  
     {
 1229  4
         return leftPad( str, size, " " );
 1230  
     }
 1231  
 
 1232  
     /**
 1233  
      * Left pad a String with a specified string. Pad to a size of n.
 1234  
      *
 1235  
      * @param str   String to pad out
 1236  
      * @param size  size to pad to
 1237  
      * @param delim String to pad with
 1238  
      * @return left padded String
 1239  
      * @throws NullPointerException if str or delim is null
 1240  
      * @throws ArithmeticException  if delim is the empty string
 1241  
      */
 1242  
     public @Nonnull static String leftPad( @Nonnull String str, int size, @Nonnull String delim )
 1243  
     {
 1244  14
         size = ( size - str.length() ) / delim.length();
 1245  10
         if ( size > 0 )
 1246  
         {
 1247  6
             str = repeat( delim, size ) + str;
 1248  
         }
 1249  10
         return str;
 1250  
     }
 1251  
 
 1252  
     // Stripping
 1253  
     //--------------------------------------------------------------------------
 1254  
 
 1255  
     /**
 1256  
      * <p>Remove whitespace from the front and back of a String.</p>
 1257  
      *
 1258  
      * @param str the String to remove whitespace from
 1259  
      * @return the stripped String
 1260  
      */
 1261  
     public static String strip( String str )
 1262  
     {
 1263  3
         return strip( str, null );
 1264  
     }
 1265  
 
 1266  
     /**
 1267  
      * <p>Remove a specified String from the front and back of a
 1268  
      * String.</p>
 1269  
      * <p/>
 1270  
      * <p>If whitespace is wanted to be removed, used the
 1271  
      * {@link #strip(java.lang.String)} method.</p>
 1272  
      *
 1273  
      * @param str   the String to remove a string from
 1274  
      * @param delim the String to remove at start and end
 1275  
      * @return the stripped String
 1276  
      */
 1277  
     public static String strip( String str, @Nullable String delim )
 1278  
     {
 1279  13
         str = stripStart( str, delim );
 1280  13
         return stripEnd( str, delim );
 1281  
     }
 1282  
 
 1283  
     /**
 1284  
      * <p>Strip whitespace from the front and back of every String
 1285  
      * in the array.</p>
 1286  
      *
 1287  
      * @param strs the Strings to remove whitespace from
 1288  
      * @return the stripped Strings
 1289  
      */
 1290  
     public static String[] stripAll( String... strs )
 1291  
     {
 1292  4
         return stripAll( strs, null );
 1293  
     }
 1294  
 
 1295  
     /**
 1296  
      * <p>Strip the specified delimiter from the front and back of
 1297  
      * every String in the array.</p>
 1298  
      *
 1299  
      * @param strs      the Strings to remove a String from
 1300  
      * @param delimiter the String to remove at start and end
 1301  
      * @return the stripped Strings
 1302  
      */
 1303  
     public static String[] stripAll( String[] strs, @Nullable String delimiter )
 1304  
     {
 1305  8
         if ( ( strs == null ) || ( strs.length == 0 ) )
 1306  
         {
 1307  4
             return strs;
 1308  
         }
 1309  4
         int sz = strs.length;
 1310  4
         String[] newArr = new String[sz];
 1311  10
         for ( int i = 0; i < sz; i++ )
 1312  
         {
 1313  6
             newArr[i] = strip( strs[i], delimiter );
 1314  
         }
 1315  4
         return newArr;
 1316  
     }
 1317  
 
 1318  
     /**
 1319  
      * <p>Strip any of a supplied String from the end of a String.</p>
 1320  
      * <p/>
 1321  
      * <p>If the strip String is <code>null</code>, whitespace is
 1322  
      * stripped.</p>
 1323  
      *
 1324  
      * @param str   the String to remove characters from
 1325  
      * @param strip the String to remove
 1326  
      * @return the stripped String
 1327  
      */
 1328  
     public static String stripEnd( String str, @Nullable String strip )
 1329  
     {
 1330  19
         if ( str == null )
 1331  
         {
 1332  5
             return null;
 1333  
         }
 1334  14
         int end = str.length();
 1335  
 
 1336  14
         if ( strip == null )
 1337  
         {
 1338  18
             while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) )
 1339  
             {
 1340  11
                 end--;
 1341  
             }
 1342  
         }
 1343  
         else
 1344  
         {
 1345  12
             while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) )
 1346  
             {
 1347  5
                 end--;
 1348  
             }
 1349  
         }
 1350  14
         return str.substring( 0, end );
 1351  
     }
 1352  
 
 1353  
     /**
 1354  
      * <p>Strip any of a supplied String from the start of a String.</p>
 1355  
      * <p/>
 1356  
      * <p>If the strip String is <code>null</code>, whitespace is
 1357  
      * stripped.</p>
 1358  
      *
 1359  
      * @param str   the String to remove characters from
 1360  
      * @param strip the String to remove
 1361  
      * @return the stripped String
 1362  
      */
 1363  
     public static String stripStart( String str, @Nullable String strip )
 1364  
     {
 1365  19
         if ( str == null )
 1366  
         {
 1367  5
             return null;
 1368  
         }
 1369  
 
 1370  14
         int start = 0;
 1371  
 
 1372  14
         int sz = str.length();
 1373  
 
 1374  14
         if ( strip == null )
 1375  
         {
 1376  14
             while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) )
 1377  
             {
 1378  7
                 start++;
 1379  
             }
 1380  
         }
 1381  
         else
 1382  
         {
 1383  14
             while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) )
 1384  
             {
 1385  7
                 start++;
 1386  
             }
 1387  
         }
 1388  14
         return str.substring( start );
 1389  
     }
 1390  
 
 1391  
     // Case conversion
 1392  
     //--------------------------------------------------------------------------
 1393  
 
 1394  
     /**
 1395  
      * <p>Convert a String to upper case, <code>null</code> String
 1396  
      * returns <code>null</code>.</p>
 1397  
      *
 1398  
      * @param str the String to uppercase
 1399  
      * @return the upper cased String
 1400  
      */
 1401  
     public static String upperCase( String str )
 1402  
     {
 1403  4
         if ( str == null )
 1404  
         {
 1405  1
             return null;
 1406  
         }
 1407  3
         return str.toUpperCase();
 1408  
     }
 1409  
 
 1410  
     /**
 1411  
      * <p>Convert a String to lower case, <code>null</code> String
 1412  
      * returns <code>null</code>.</p>
 1413  
      *
 1414  
      * @param str the string to lowercase
 1415  
      * @return the lower cased String
 1416  
      */
 1417  
     public static String lowerCase( String str )
 1418  
     {
 1419  3
         if ( str == null )
 1420  
         {
 1421  1
             return null;
 1422  
         }
 1423  2
         return str.toLowerCase();
 1424  
     }
 1425  
 
 1426  
     /**
 1427  
      * <p>Uncapitalise a String.</p>
 1428  
      * <p/>
 1429  
      * <p>That is, convert the first character into lower-case.
 1430  
      * <code>null</code> is returned as <code>null</code>.</p>
 1431  
      *
 1432  
      * @param str the String to uncapitalise
 1433  
      * @return uncapitalised String
 1434  
      */
 1435  
     public static String uncapitalise( String str )
 1436  
     {
 1437  5
         if ( str == null )
 1438  
         {
 1439  1
             return null;
 1440  
         }
 1441  4
         else if ( str.length() == 0 )
 1442  
         {
 1443  0
             return "";
 1444  
         }
 1445  
         else
 1446  
         {
 1447  4
             return String.valueOf( Character.toLowerCase( str.charAt( 0 ) ) ) + str.substring( 1 );
 1448  
         }
 1449  
     }
 1450  
 
 1451  
     /**
 1452  
      * <p>Capitalise a String.</p>
 1453  
      * <p/>
 1454  
      * <p>That is, convert the first character into title-case.
 1455  
      * <code>null</code> is returned as <code>null</code>.</p>
 1456  
      *
 1457  
      * @param str the String to capitalise
 1458  
      * @return capitalised String
 1459  
      */
 1460  
     public static String capitalise( String str )
 1461  
     {
 1462  2
         if ( str == null )
 1463  
         {
 1464  1
             return null;
 1465  
         }
 1466  1
         else if ( str.length() == 0 )
 1467  
         {
 1468  0
             return "";
 1469  
         }
 1470  
         else
 1471  
         {
 1472  1
             return String.valueOf( Character.toTitleCase( str.charAt( 0 ) ) ) + str.substring( 1 );
 1473  
         }
 1474  
     }
 1475  
 
 1476  
     /**
 1477  
      * <p>Swaps the case of String.</p>
 1478  
      * <p/>
 1479  
      * <p>Properly looks after making sure the start of words
 1480  
      * are Titlecase and not Uppercase.</p>
 1481  
      * <p/>
 1482  
      * <p><code>null</code> is returned as <code>null</code>.</p>
 1483  
      *
 1484  
      * @param str the String to swap the case of
 1485  
      * @return the modified String
 1486  
      */
 1487  
     public static String swapCase( String str )
 1488  
     {
 1489  3
         if ( str == null )
 1490  
         {
 1491  1
             return null;
 1492  
         }
 1493  2
         int sz = str.length();
 1494  2
         StringBuilder buffer = new StringBuilder( sz );
 1495  
 
 1496  2
         boolean whitespace = false;
 1497  
         char ch;
 1498  
         char tmp;
 1499  
 
 1500  12
         for ( int i = 0; i < sz; i++ )
 1501  
         {
 1502  10
             ch = str.charAt( i );
 1503  10
             if ( Character.isUpperCase( ch ) )
 1504  
             {
 1505  2
                 tmp = Character.toLowerCase( ch );
 1506  
             }
 1507  8
             else if ( Character.isTitleCase( ch ) )
 1508  
             {
 1509  0
                 tmp = Character.toLowerCase( ch );
 1510  
             }
 1511  8
             else if ( Character.isLowerCase( ch ) )
 1512  
             {
 1513  8
                 if ( whitespace )
 1514  
                 {
 1515  0
                     tmp = Character.toTitleCase( ch );
 1516  
                 }
 1517  
                 else
 1518  
                 {
 1519  8
                     tmp = Character.toUpperCase( ch );
 1520  
                 }
 1521  
             }
 1522  
             else
 1523  
             {
 1524  0
                 tmp = ch;
 1525  
             }
 1526  10
             buffer.append( tmp );
 1527  10
             whitespace = Character.isWhitespace( ch );
 1528  
         }
 1529  2
         return buffer.toString();
 1530  
     }
 1531  
 
 1532  
 
 1533  
     /**
 1534  
      * <p>Capitalise all the words in a String.</p>
 1535  
      * <p/>
 1536  
      * <p>Uses {@link Character#isWhitespace(char)} as a
 1537  
      * separator between words.</p>
 1538  
      * <p/>
 1539  
      * <p><code>null</code> will return <code>null</code>.</p>
 1540  
      *
 1541  
      * @param str the String to capitalise
 1542  
      * @return capitalised String
 1543  
      */
 1544  
     public static String capitaliseAllWords( String str )
 1545  
     {
 1546  2
         if ( str == null )
 1547  
         {
 1548  1
             return null;
 1549  
         }
 1550  1
         int sz = str.length();
 1551  1
         StringBuilder buffer = new StringBuilder( sz );
 1552  1
         boolean space = true;
 1553  14
         for ( int i = 0; i < sz; i++ )
 1554  
         {
 1555  13
             char ch = str.charAt( i );
 1556  13
             if ( Character.isWhitespace( ch ) )
 1557  
             {
 1558  2
                 buffer.append( ch );
 1559  2
                 space = true;
 1560  
             }
 1561  11
             else if ( space )
 1562  
             {
 1563  3
                 buffer.append( Character.toTitleCase( ch ) );
 1564  3
                 space = false;
 1565  
             }
 1566  
             else
 1567  
             {
 1568  8
                 buffer.append( ch );
 1569  
             }
 1570  
         }
 1571  1
         return buffer.toString();
 1572  
     }
 1573  
 
 1574  
     /**
 1575  
      * <p>Uncapitalise all the words in a string.</p>
 1576  
      * <p/>
 1577  
      * <p>Uses {@link Character#isWhitespace(char)} as a
 1578  
      * separator between words.</p>
 1579  
      * <p/>
 1580  
      * <p><code>null</code> will return <code>null</code>.</p>
 1581  
      *
 1582  
      * @param str the string to uncapitalise
 1583  
      * @return uncapitalised string
 1584  
      */
 1585  
     public static String uncapitaliseAllWords( String str )
 1586  
     {
 1587  5
         if ( str == null )
 1588  
         {
 1589  1
             return null;
 1590  
         }
 1591  4
         int sz = str.length();
 1592  4
         StringBuilder buffer = new StringBuilder( sz );
 1593  4
         boolean space = true;
 1594  37
         for ( int i = 0; i < sz; i++ )
 1595  
         {
 1596  33
             char ch = str.charAt( i );
 1597  33
             if ( Character.isWhitespace( ch ) )
 1598  
             {
 1599  6
                 buffer.append( ch );
 1600  6
                 space = true;
 1601  
             }
 1602  27
             else if ( space )
 1603  
             {
 1604  6
                 buffer.append( Character.toLowerCase( ch ) );
 1605  6
                 space = false;
 1606  
             }
 1607  
             else
 1608  
             {
 1609  21
                 buffer.append( ch );
 1610  
             }
 1611  
         }
 1612  4
         return buffer.toString();
 1613  
     }
 1614  
 
 1615  
     // Nested extraction
 1616  
     //--------------------------------------------------------------------------
 1617  
 
 1618  
     /**
 1619  
      * <p>Get the String that is nested in between two instances of the
 1620  
      * same String.</p>
 1621  
      * <p/>
 1622  
      * <p>If <code>str</code> is <code>null</code>, will
 1623  
      * return <code>null</code>.</p>
 1624  
      *
 1625  
      * @param str the String containing nested-string
 1626  
      * @param tag the String before and after nested-string
 1627  
      * @return the String that was nested, or <code>null</code>
 1628  
      * @throws NullPointerException if tag is <code>null</code>
 1629  
      */
 1630  
     public static String getNestedString( String str, @Nonnull String tag )
 1631  
     {
 1632  4
         return getNestedString( str, tag, tag );
 1633  
     }
 1634  
 
 1635  
     /**
 1636  
      * <p>Get the String that is nested in between two Strings.</p>
 1637  
      *
 1638  
      * @param str   the String containing nested-string
 1639  
      * @param open  the String before nested-string
 1640  
      * @param close the String after nested-string
 1641  
      * @return the String that was nested, or <code>null</code>
 1642  
      * @throws NullPointerException if open or close is <code>null</code>
 1643  
      */
 1644  
     public static String getNestedString( String str, @Nonnull String open, @Nonnull String close )
 1645  
     {
 1646  10
         if ( str == null )
 1647  
         {
 1648  2
             return null;
 1649  
         }
 1650  8
         int start = str.indexOf( open );
 1651  5
         if ( start != -1 )
 1652  
         {
 1653  2
             int end = str.indexOf( close, start + open.length() );
 1654  2
             if ( end != -1 )
 1655  
             {
 1656  2
                 return str.substring( start + open.length(), end );
 1657  
             }
 1658  
         }
 1659  3
         return null;
 1660  
     }
 1661  
 
 1662  
     /**
 1663  
      * <p>How many times is the substring in the larger String.</p>
 1664  
      * <p/>
 1665  
      * <p><code>null</code> returns <code>0</code>.</p>
 1666  
      *
 1667  
      * @param str the String to check
 1668  
      * @param sub the substring to count
 1669  
      * @return the number of occurances, 0 if the String is <code>null</code>
 1670  
      * @throws NullPointerException if sub is <code>null</code>
 1671  
      */
 1672  
     public static int countMatches( @Nullable String str, @Nonnull String sub )
 1673  
     {
 1674  17
         if ( sub.equals( "" ) )
 1675  
         {
 1676  0
             return 0;
 1677  
         }
 1678  15
         if ( str == null )
 1679  
         {
 1680  1
             return 0;
 1681  
         }
 1682  14
         int count = 0;
 1683  14
         int idx = 0;
 1684  63
         while ( ( idx = str.indexOf( sub, idx ) ) != -1 )
 1685  
         {
 1686  49
             count++;
 1687  49
             idx += sub.length();
 1688  
         }
 1689  14
         return count;
 1690  
     }
 1691  
 
 1692  
     // Character Tests
 1693  
     //--------------------------------------------------------------------------
 1694  
 
 1695  
     /**
 1696  
      * <p>Checks if the String contains only unicode letters.</p>
 1697  
      * <p/>
 1698  
      * <p><code>null</code> will return <code>false</code>.
 1699  
      * An empty String will return <code>true</code>.</p>
 1700  
      *
 1701  
      * @param str the String to check
 1702  
      * @return <code>true</code> if only contains letters, and is non-null
 1703  
      */
 1704  
     public static boolean isAlpha( String str )
 1705  
     {
 1706  5
         if ( str == null )
 1707  
         {
 1708  1
             return false;
 1709  
         }
 1710  4
         int sz = str.length();
 1711  22
         for ( int i = 0; i < sz; i++ )
 1712  
         {
 1713  21
             if ( !Character.isLetter( str.charAt( i ) ) )
 1714  
             {
 1715  3
                 return false;
 1716  
             }
 1717  
         }
 1718  1
         return true;
 1719  
     }
 1720  
 
 1721  
     /**
 1722  
      * <p>Checks if the String contains only whitespace.</p>
 1723  
      * <p/>
 1724  
      * <p><code>null</code> will return <code>false</code>. An
 1725  
      * empty String will return <code>true</code>.</p>
 1726  
      *
 1727  
      * @param str the String to check
 1728  
      * @return <code>true</code> if only contains whitespace, and is non-null
 1729  
      */
 1730  
     public static boolean isWhitespace( String str )
 1731  
     {
 1732  7
         if ( str == null )
 1733  
         {
 1734  1
             return false;
 1735  
         }
 1736  6
         int sz = str.length();
 1737  16
         for ( int i = 0; i < sz; i++ )
 1738  
         {
 1739  12
             if ( ( !Character.isWhitespace( str.charAt( i ) ) ) )
 1740  
             {
 1741  2
                 return false;
 1742  
             }
 1743  
         }
 1744  4
         return true;
 1745  
     }
 1746  
 
 1747  
     /**
 1748  
      * <p>Checks if the String contains only unicode letters and
 1749  
      * space (<code>' '</code>).</p>
 1750  
      * <p/>
 1751  
      * <p><code>null</code> will return <code>false</code>. An
 1752  
      * empty String will return <code>true</code>.</p>
 1753  
      *
 1754  
      * @param str the String to check
 1755  
      * @return <code>true</code> if only contains letters and space,
 1756  
      *         and is non-null
 1757  
      */
 1758  
     public static boolean isAlphaSpace( String str )
 1759  
     {
 1760  6
         if ( str == null )
 1761  
         {
 1762  1
             return false;
 1763  
         }
 1764  5
         int sz = str.length();
 1765  35
         for ( int i = 0; i < sz; i++ )
 1766  
         {
 1767  33
             if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
 1768  
             {
 1769  3
                 return false;
 1770  
             }
 1771  
         }
 1772  2
         return true;
 1773  
     }
 1774  
 
 1775  
     /**
 1776  
      * <p>Checks if the String contains only unicode letters or digits.</p>
 1777  
      * <p/>
 1778  
      * <p><code>null</code> will return <code>false</code>. An empty
 1779  
      * String will return <code>true</code>.</p>
 1780  
      *
 1781  
      * @param str the String to check
 1782  
      * @return <code>true</code> if only contains letters or digits,
 1783  
      *         and is non-null
 1784  
      */
 1785  
     public static boolean isAlphanumeric( String str )
 1786  
     {
 1787  9
         if ( str == null )
 1788  
         {
 1789  1
             return false;
 1790  
         }
 1791  8
         int sz = str.length();
 1792  62
         for ( int i = 0; i < sz; i++ )
 1793  
         {
 1794  58
             if ( !Character.isLetterOrDigit( str.charAt( i ) ) )
 1795  
             {
 1796  4
                 return false;
 1797  
             }
 1798  
         }
 1799  4
         return true;
 1800  
     }
 1801  
 
 1802  
     /**
 1803  
      * <p>Checks if the String contains only unicode letters, digits
 1804  
      * or space (<code>' '</code>).</p>
 1805  
      * <p/>
 1806  
      * <p><code>null</code> will return <code>false</code>. An empty
 1807  
      * String will return <code>true</code>.</p>
 1808  
      *
 1809  
      * @param str the String to check
 1810  
      * @return <code>true</code> if only contains letters, digits or space,
 1811  
      *         and is non-null
 1812  
      */
 1813  
     public static boolean isAlphanumericSpace( String str )
 1814  
     {
 1815  9
         if ( str == null )
 1816  
         {
 1817  1
             return false;
 1818  
         }
 1819  8
         int sz = str.length();
 1820  66
         for ( int i = 0; i < sz; i++ )
 1821  
         {
 1822  60
             if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
 1823  
             {
 1824  2
                 return false;
 1825  
             }
 1826  
         }
 1827  6
         return true;
 1828  
     }
 1829  
 
 1830  
     /**
 1831  
      * <p>Checks if the String contains only unicode digits.</p>
 1832  
      * <p/>
 1833  
      * <p><code>null</code> will return <code>false</code>.
 1834  
      * An empty String will return <code>true</code>.</p>
 1835  
      *
 1836  
      * @param str the String to check
 1837  
      * @return <code>true</code> if only contains digits, and is non-null
 1838  
      */
 1839  
     public static boolean isNumeric( String str )
 1840  
     {
 1841  9
         if ( str == null )
 1842  
         {
 1843  1
             return false;
 1844  
         }
 1845  8
         int sz = str.length();
 1846  21
         for ( int i = 0; i < sz; i++ )
 1847  
         {
 1848  19
             if ( !Character.isDigit( str.charAt( i ) ) )
 1849  
             {
 1850  6
                 return false;
 1851  
             }
 1852  
         }
 1853  2
         return true;
 1854  
     }
 1855  
 
 1856  
     // Defaults
 1857  
     //--------------------------------------------------------------------------
 1858  
 
 1859  
     /**
 1860  
      * <p>Returns either the passed in <code>Object</code> as a String,
 1861  
      * or, if the <code>Object</code> is <code>null</code>, an empty
 1862  
      * String.</p>
 1863  
      *
 1864  
      * @param obj the Object to check
 1865  
      * @return the passed in Object's toString, or blank if it was
 1866  
      *         <code>null</code>
 1867  
      */
 1868  
     public @Nonnull static String defaultString( Object obj )
 1869  
     {
 1870  2
         return defaultString( obj, "" );
 1871  
     }
 1872  
 
 1873  
     /**
 1874  
      * <p>Returns either the passed in <code>Object</code> as a String,
 1875  
      * or, if the <code>Object</code> is <code>null</code>, a passed
 1876  
      * in default String.</p>
 1877  
      *
 1878  
      * @param obj           the Object to check
 1879  
      * @param defaultString the default String to return if str is
 1880  
      *                      <code>null</code>
 1881  
      * @return the passed in string, or the default if it was
 1882  
      *         <code>null</code>
 1883  
      */
 1884  
     public @Nonnull static String defaultString( Object obj, @Nonnull String defaultString )
 1885  
     {
 1886  4
         return ( obj == null ) ? defaultString : obj.toString();
 1887  
     }
 1888  
 
 1889  
     // Reversing
 1890  
     //--------------------------------------------------------------------------
 1891  
 
 1892  
     /**
 1893  
      * <p>Reverse a String.</p>
 1894  
      * <p/>
 1895  
      * <p><code>null</code> String returns <code>null</code>.</p>
 1896  
      *
 1897  
      * @param str the String to reverse
 1898  
      * @return the reversed String
 1899  
      */
 1900  
     public static String reverse( String str )
 1901  
     {
 1902  4
         if ( str == null )
 1903  
         {
 1904  1
             return null;
 1905  
         }
 1906  3
         return new StringBuffer( str ).reverse().toString();
 1907  
     }
 1908  
 
 1909  
     /**
 1910  
      * <p>Reverses a String that is delimited by a specific character.</p>
 1911  
      * <p/>
 1912  
      * <p>The Strings between the delimiters are not reversed.
 1913  
      * Thus java.lang.String becomes String.lang.java (if the delimiter
 1914  
      * is <code>'.'</code>).</p>
 1915  
      *
 1916  
      * @param str       the String to reverse
 1917  
      * @param delimiter the delimiter to use
 1918  
      * @return the reversed String
 1919  
      */
 1920  
     public @Nonnull static String reverseDelimitedString( @Nonnull String str, String delimiter )
 1921  
     {
 1922  
         // could implement manually, but simple way is to reuse other,
 1923  
         // probably slower, methods.
 1924  7
         String[] strs = split( str, delimiter );
 1925  5
         reverseArray( strs );
 1926  5
         return join( strs, delimiter );
 1927  
     }
 1928  
 
 1929  
     /**
 1930  
      * <p>Reverses an array.</p>
 1931  
      * <p/>
 1932  
      * <p>TAKEN FROM CollectionsUtils.</p>
 1933  
      *
 1934  
      * @param array the array to reverse
 1935  
      */
 1936  
     private static void reverseArray( @Nonnull String... array )
 1937  
     {
 1938  5
         int i = 0;
 1939  5
         int j = array.length - 1;
 1940  
         String tmp;
 1941  
 
 1942  6
         while ( j > i )
 1943  
         {
 1944  1
             tmp = array[j];
 1945  1
             array[j] = array[i];
 1946  1
             array[i] = tmp;
 1947  1
             j--;
 1948  1
             i++;
 1949  
         }
 1950  5
     }
 1951  
 
 1952  
     // Abbreviating
 1953  
     //--------------------------------------------------------------------------
 1954  
 
 1955  
     /**
 1956  
      * Turn "Now is the time for all good men" into "Now is the time for..."
 1957  
      * <p/>
 1958  
      * Specifically:
 1959  
      * <p/>
 1960  
      * If str is less than max characters long, return it.
 1961  
      * Else abbreviate it to (substring(str, 0, max-3) + "...").
 1962  
      * If maxWidth is less than 3, throw an IllegalArgumentException.
 1963  
      * In no case will it return a string of length greater than maxWidth.
 1964  
      *
 1965  
      * @param maxWidth maximum length of result string
 1966  
      */
 1967  
     public @Nonnull static String abbreviate( @Nonnull String s, int maxWidth )
 1968  
     {
 1969  5
         return abbreviate( s, 0, maxWidth );
 1970  
     }
 1971  
 
 1972  
     /**
 1973  
      * Turn "Now is the time for all good men" into "...is the time for..."
 1974  
      * <p/>
 1975  
      * Works like abbreviate(String, int), but allows you to specify a "left edge"
 1976  
      * offset.  Note that this left edge is not necessarily going to be the leftmost
 1977  
      * character in the result, or the first
 1978  
      * character following the ellipses, but it will appear somewhere in the result.
 1979  
      * In no case will it return a string of length greater than maxWidth.
 1980  
      *
 1981  
      * @param offset   left edge of source string
 1982  
      * @param maxWidth maximum length of result string
 1983  
      */
 1984  
     public @Nonnull static String abbreviate( @Nonnull String s, int offset, int maxWidth )
 1985  
     {
 1986  10
         if ( maxWidth < 4 )
 1987  
         {
 1988  2
             throw new IllegalArgumentException( "Minimum abbreviation width is 4" );
 1989  
         }
 1990  8
         if ( s.length() <= maxWidth )
 1991  
         {
 1992  3
             return s;
 1993  
         }
 1994  3
         if ( offset > s.length() )
 1995  
         {
 1996  0
             offset = s.length();
 1997  
         }
 1998  3
         if ( ( s.length() - offset ) < ( maxWidth - 3 ) )
 1999  
         {
 2000  0
             offset = s.length() - ( maxWidth - 3 );
 2001  
         }
 2002  3
         if ( offset <= 4 )
 2003  
         {
 2004  2
             return s.substring( 0, maxWidth - 3 ) + "...";
 2005  
         }
 2006  1
         if ( maxWidth < 7 )
 2007  
         {
 2008  0
             throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" );
 2009  
         }
 2010  1
         if ( ( offset + ( maxWidth - 3 ) ) < s.length() )
 2011  
         {
 2012  1
             return "..." + abbreviate( s.substring( offset ), maxWidth - 3 );
 2013  
         }
 2014  0
         return "..." + s.substring( s.length() - ( maxWidth - 3 ) );
 2015  
     }
 2016  
 
 2017  
     // Difference
 2018  
     //--------------------------------------------------------------------------
 2019  
 
 2020  
     /**
 2021  
      * Compare two strings, and return the portion where they differ.
 2022  
      * (More precisely, return the remainder of the second string,
 2023  
      * starting from where it's different from the first.)
 2024  
      * <p/>
 2025  
      * E.g. strdiff("i am a machine", "i am a robot") -> "robot"
 2026  
      *
 2027  
      * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal
 2028  
      */
 2029  
     public static String difference( @Nonnull String s1, @Nonnull String s2 )
 2030  
     {
 2031  6
         int at = differenceAt( s1, s2 );
 2032  3
         if ( at == -1 )
 2033  
         {
 2034  0
             return "";
 2035  
         }
 2036  3
         return s2.substring( at );
 2037  
     }
 2038  
 
 2039  
     /**
 2040  
      * Compare two strings, and return the index at which the strings begin to differ.
 2041  
      * <p>
 2042  
      * E.g. strdiff("i am a machine", "i am a robot") -> 7
 2043  
      * </p>
 2044  
      *
 2045  
      * @return the index where s2 and s1 begin to differ; -1 if they are equal
 2046  
      */
 2047  
     public static int differenceAt( @Nonnull String s1, @Nonnull String s2 )
 2048  
     {
 2049  
         int i;
 2050  26
         for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i )
 2051  
         {
 2052  20
             if ( s1.charAt( i ) != s2.charAt( i ) )
 2053  
             {
 2054  6
                 break;
 2055  
             }
 2056  
         }
 2057  6
         if ( ( i < s2.length() ) || ( i < s1.length() ) )
 2058  
         {
 2059  6
             return i;
 2060  
         }
 2061  0
         return -1;
 2062  
     }
 2063  
 
 2064  
     /**
 2065  
      * Fill all 'variables' in the given text with the values from the map.
 2066  
      * Any text looking like '${key}' will get replaced by the value stored
 2067  
      * in the namespace map under the 'key'.
 2068  
      *
 2069  
      * @param text
 2070  
      * @param namespace
 2071  
      * @return the interpolated text.
 2072  
      */
 2073  
     public static String interpolate( String text, @Nonnull Map<?, ?> namespace )
 2074  
     {
 2075  4
         for ( Map.Entry<?, ?> entry : namespace.entrySet() )
 2076  
         {
 2077  1
             String key = entry.getKey().toString();
 2078  
 
 2079  1
             Object obj = entry.getValue();
 2080  
 
 2081  1
             if ( obj == null )
 2082  
             {
 2083  0
                 throw new NullPointerException( "The value of the key '" + key + "' is null." );
 2084  
             }
 2085  
 
 2086  1
             String value = obj.toString();
 2087  
 
 2088  1
             text = replace( text, "${" + key + "}", value );
 2089  
 
 2090  1
             if ( !key.contains( " " ) )
 2091  
             {
 2092  1
                 text = replace( text, "$" + key, value );
 2093  
             }
 2094  1
         }
 2095  2
         return text;
 2096  
     }
 2097  
 
 2098  
     /**
 2099  
      * This is basically the inverse of {@link #addAndDeHump(String)}.
 2100  
      * It will remove the 'replaceThis' parameter and uppercase the next
 2101  
      * character afterwards.
 2102  
      * <pre>
 2103  
      * removeAndHump( &quot;this-is-it&quot;, %quot;-&quot; );
 2104  
      * </pre>
 2105  
      * will become 'ThisIsIt'.
 2106  
      *
 2107  
      * @param data
 2108  
      * @param replaceThis
 2109  
      * @return humped String
 2110  
      */
 2111  
     public @Nonnull static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis )
 2112  
     {
 2113  
         String temp;
 2114  
 
 2115  6
         StringBuilder out = new StringBuilder();
 2116  
 
 2117  6
         temp = data;
 2118  
 
 2119  6
         StringTokenizer st = new StringTokenizer( temp, replaceThis );
 2120  
 
 2121  11
         while ( st.hasMoreTokens() )
 2122  
         {
 2123  7
             String element = st.nextToken();
 2124  
 
 2125  7
             out.append( capitalizeFirstLetter( element ) );
 2126  7
         }
 2127  
 
 2128  3
         return out.toString();
 2129  
     }
 2130  
 
 2131  
     /**
 2132  
      * Convert the first character of the given String to uppercase.
 2133  
      * This method will <i>not</i> trim of spaces!
 2134  
      * <p/>
 2135  
      * <p>
 2136  
      * <b>Attention:</b> this method will currently throw a
 2137  
      * <code>IndexOutOfBoundsException</code> for empty strings!
 2138  
      * </p>
 2139  
      *
 2140  
      * @param data the String to get capitalized
 2141  
      * @return data string with the first character transformed to uppercase
 2142  
      * @throws NullPointerException if data is <code>null</code>
 2143  
      */
 2144  
     public @Nonnull static String capitalizeFirstLetter( @Nonnull String data )
 2145  
     {
 2146  33
         char firstLetter = Character.toTitleCase( data.substring( 0, 1 ).charAt( 0 ) );
 2147  
 
 2148  32
         String restLetters = data.substring( 1 );
 2149  
 
 2150  32
         return firstLetter + restLetters;
 2151  
     }
 2152  
 
 2153  
     /**
 2154  
      * Convert the first character of the given String to lowercase.
 2155  
      * This method will <i>not</i> trim of spaces!
 2156  
      * <p/>
 2157  
      * <p>
 2158  
      * <b>Attention:</b> this method will currently throw a
 2159  
      * <code>IndexOutOfBoundsException</code> for empty strings!
 2160  
      * </p>
 2161  
      *
 2162  
      * @param data the String to get it's first character lower-cased.
 2163  
      * @return data string with the first character transformed to lowercase
 2164  
      * @throws NullPointerException if data is <code>null</code>
 2165  
      */
 2166  
     public @Nonnull static String lowercaseFirstLetter( @Nonnull String data )
 2167  
     {
 2168  2
         char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) );
 2169  
 
 2170  1
         String restLetters = data.substring( 1 );
 2171  
 
 2172  1
         return firstLetter + restLetters;
 2173  
     }
 2174  
 
 2175  
     /**
 2176  
      * Take the input string and un-camel-case it.
 2177  
      * <p/>
 2178  
      * 'ThisIsIt' will become 'this-is-it'.
 2179  
      *
 2180  
      * @param view
 2181  
      * @return deHumped String
 2182  
      */
 2183  
     public @Nonnull static String addAndDeHump( @Nonnull String view )
 2184  
     {
 2185  4
         StringBuilder sb = new StringBuilder();
 2186  
 
 2187  24
         for ( int i = 0; i < view.length(); i++ )
 2188  
         {
 2189  20
             if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) )
 2190  
             {
 2191  9
                 sb.append( '-' );
 2192  
             }
 2193  
 
 2194  20
             sb.append( view.charAt( i ) );
 2195  
         }
 2196  
 
 2197  3
         return sb.toString().trim().toLowerCase( Locale.ENGLISH );
 2198  
     }
 2199  
 
 2200  
     /**
 2201  
      * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
 2202  
      * <p/>
 2203  
      * <pre>
 2204  
      * StringUtils.quoteAndEscape(null, *)    = null
 2205  
      * StringUtils.quoteAndEscape("", *)      = ""
 2206  
      * StringUtils.quoteAndEscape("abc", '"') = abc
 2207  
      * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
 2208  
      * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
 2209  
      * </pre>
 2210  
      *
 2211  
      * @param source
 2212  
      * @param quoteChar
 2213  
      * @return the String quoted and escaped
 2214  
      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
 2215  
      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
 2216  
      * 
 2217  
      */
 2218  
     public static String quoteAndEscape( @Nullable String source, char quoteChar )
 2219  
     {
 2220  6
         return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false );
 2221  
     }
 2222  
 
 2223  
     /**
 2224  
      * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
 2225  
      *
 2226  
      * @param source
 2227  
      * @param quoteChar
 2228  
      * @param quotingTriggers
 2229  
      * @return the String quoted and escaped
 2230  
      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
 2231  
      * 
 2232  
      */
 2233  
     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers )
 2234  
     {
 2235  31
         return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false );
 2236  
     }
 2237  
 
 2238  
     /**
 2239  
      * @param source
 2240  
      * @param quoteChar
 2241  
      * @param escapedChars
 2242  
      * @param escapeChar
 2243  
      * @param force
 2244  
      * @return the String quoted and escaped
 2245  
      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
 2246  
      * 
 2247  
      */
 2248  
     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, char escapeChar,
 2249  
                                          boolean force )
 2250  
     {
 2251  14
         return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force );
 2252  
     }
 2253  
 
 2254  
     /**
 2255  
      * @param source
 2256  
      * @param quoteChar
 2257  
      * @param escapedChars
 2258  
      * @param quotingTriggers
 2259  
      * @param escapeChar
 2260  
      * @param force
 2261  
      * @return the String quoted and escaped
 2262  
      * 
 2263  
      */
 2264  
     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars,
 2265  
                                          @Nonnull final char[] quotingTriggers, char escapeChar, boolean force )
 2266  
     {
 2267  102
         if ( source == null )
 2268  
         {
 2269  6
             return null;
 2270  
         }
 2271  
 
 2272  96
         if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith(
 2273  
             Character.toString( quoteChar ) ) )
 2274  
         {
 2275  2
             return source;
 2276  
         }
 2277  
 
 2278  94
         String escaped = escape( source, escapedChars, escapeChar );
 2279  
 
 2280  94
         boolean quote = false;
 2281  94
         if ( force )
 2282  
         {
 2283  12
             quote = true;
 2284  
         }
 2285  82
         else if ( !escaped.equals( source ) )
 2286  
         {
 2287  7
             quote = true;
 2288  
         }
 2289  
         else
 2290  
         {
 2291  753
             for ( char quotingTrigger : quotingTriggers )
 2292  
             {
 2293  705
                 if ( escaped.indexOf( quotingTrigger ) > -1 )
 2294  
                 {
 2295  27
                     quote = true;
 2296  27
                     break;
 2297  
                 }
 2298  
             }
 2299  
         }
 2300  
 
 2301  94
         if ( quote )
 2302  
         {
 2303  46
             return quoteChar + escaped + quoteChar;
 2304  
         }
 2305  
 
 2306  48
         return escaped;
 2307  
     }
 2308  
 
 2309  
     /**
 2310  
      * @param source
 2311  
      * @param escapedChars
 2312  
      * @param escapeChar
 2313  
      * @return the String escaped
 2314  
      * 
 2315  
      */
 2316  
     public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar )
 2317  
     {
 2318  99
         if ( source == null )
 2319  
         {
 2320  1
             return null;
 2321  
         }
 2322  
 
 2323  98
         char[] eqc = new char[escapedChars.length];
 2324  98
         System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length );
 2325  98
         Arrays.sort( eqc );
 2326  
 
 2327  98
         StringBuilder buffer = new StringBuilder( source.length() );
 2328  
 
 2329  591
         for ( int i = 0; i < source.length(); i++ )
 2330  
         {
 2331  493
             final char c = source.charAt( i );
 2332  493
             int result = Arrays.binarySearch( eqc, c );
 2333  
 
 2334  493
             if ( result > -1 )
 2335  
             {
 2336  13
                 buffer.append( escapeChar );
 2337  
             }
 2338  493
             buffer.append( c );
 2339  
         }
 2340  
 
 2341  98
         return buffer.toString();
 2342  
     }
 2343  
 
 2344  
     /**
 2345  
      * Remove all duplicate whitespace characters and line terminators are replaced with a single
 2346  
      * space.
 2347  
      *
 2348  
      * @param s a not null String
 2349  
      * @return a string with unique whitespace.
 2350  
      * 
 2351  
      */
 2352  
     public @Nonnull static String removeDuplicateWhitespace( @Nonnull String s )
 2353  
     {
 2354  5
         StringBuilder result = new StringBuilder();
 2355  5
         int length = s.length();
 2356  4
         boolean isPreviousWhiteSpace = false;
 2357  42
         for ( int i = 0; i < length; i++ )
 2358  
         {
 2359  38
             char c = s.charAt( i );
 2360  38
             boolean thisCharWhiteSpace = Character.isWhitespace( c );
 2361  38
             if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) )
 2362  
             {
 2363  35
                 result.append( c );
 2364  
             }
 2365  38
             isPreviousWhiteSpace = thisCharWhiteSpace;
 2366  
         }
 2367  4
         return result.toString();
 2368  
     }
 2369  
 
 2370  
     /**
 2371  
      * Parses the given String and replaces all occurrences of
 2372  
      * '\n', '\r' and '\r\n' with the system line separator.
 2373  
      *
 2374  
      * @param s a not null String
 2375  
      * @return a String that contains only System line separators.
 2376  
      * @see #unifyLineSeparators(String, String)
 2377  
      * 
 2378  
      */
 2379  
     public static String unifyLineSeparators( @Nullable String s )
 2380  
     {
 2381  6
         return unifyLineSeparators( s, System.getProperty( "line.separator" ) );
 2382  
     }
 2383  
 
 2384  
     /**
 2385  
      * Parses the given String and replaces all occurrences of
 2386  
      * '\n', '\r' and '\r\n' with the system line separator.
 2387  
      *
 2388  
      * @param s  a not null String
 2389  
      * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator.
 2390  
      * @return a String that contains only System line separators.
 2391  
      * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters.
 2392  
      * 
 2393  
      */
 2394  
     public static String unifyLineSeparators( @Nullable String s, @Nullable String ls )
 2395  
     {
 2396  10
         if ( s == null )
 2397  
         {
 2398  2
             return null;
 2399  
         }
 2400  
 
 2401  8
         if ( ls == null )
 2402  
         {
 2403  1
             ls = System.getProperty( "line.separator" );
 2404  
         }
 2405  
 
 2406  8
         if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) )
 2407  
         {
 2408  0
             throw new IllegalArgumentException( "Requested line separator is invalid." );
 2409  
         }
 2410  
 
 2411  8
         int length = s.length();
 2412  
 
 2413  8
         StringBuilder buffer = new StringBuilder( length );
 2414  214
         for ( int i = 0; i < length; i++ )
 2415  
         {
 2416  206
             if ( s.charAt( i ) == '\r' )
 2417  
             {
 2418  2
                 if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' )
 2419  
                 {
 2420  2
                     i++;
 2421  
                 }
 2422  
 
 2423  2
                 buffer.append( ls );
 2424  
             }
 2425  204
             else if ( s.charAt( i ) == '\n' )
 2426  
             {
 2427  12
                 buffer.append( ls );
 2428  
             }
 2429  
             else
 2430  
             {
 2431  192
                 buffer.append( s.charAt( i ) );
 2432  
             }
 2433  
         }
 2434  
 
 2435  8
         return buffer.toString();
 2436  
     }
 2437  
 
 2438  
     /**
 2439  
      * <p>Checks if String contains a search character, handling <code>null</code>.
 2440  
      * This method uses {@link String#indexOf(int)}.</p>
 2441  
      * <p/>
 2442  
      * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p>
 2443  
      * <p/>
 2444  
      * <pre>
 2445  
      * StringUtils.contains(null, *)    = false
 2446  
      * StringUtils.contains("", *)      = false
 2447  
      * StringUtils.contains("abc", 'a') = true
 2448  
      * StringUtils.contains("abc", 'z') = false
 2449  
      * </pre>
 2450  
      *
 2451  
      * @param str        the String to check, may be null
 2452  
      * @param searchChar the character to find
 2453  
      * @return true if the String contains the search character,
 2454  
      *         false if not or <code>null</code> string input
 2455  
      * 
 2456  
      */
 2457  
     @SuppressWarnings( "ConstantConditions" )
 2458  
     public static boolean contains( @Nullable String str, char searchChar )
 2459  
     {
 2460  1
         return !isEmpty( str ) && str.indexOf( searchChar ) >= 0;
 2461  
     }
 2462  
 
 2463  
     /**
 2464  
      * <p>Checks if String contains a search String, handling <code>null</code>.
 2465  
      * This method uses {@link String#indexOf(int)}.</p>
 2466  
      * <p/>
 2467  
      * <p>A <code>null</code> String will return <code>false</code>.</p>
 2468  
      * <p/>
 2469  
      * <pre>
 2470  
      * StringUtils.contains(null, *)     = false
 2471  
      * StringUtils.contains(*, null)     = false
 2472  
      * StringUtils.contains("", "")      = true
 2473  
      * StringUtils.contains("abc", "")   = true
 2474  
      * StringUtils.contains("abc", "a")  = true
 2475  
      * StringUtils.contains("abc", "z")  = false
 2476  
      * </pre>
 2477  
      *
 2478  
      * @param str       the String to check, may be null
 2479  
      * @param searchStr the String to find, may be null
 2480  
      * @return true if the String contains the search String,
 2481  
      *         false if not or <code>null</code> string input
 2482  
      * 
 2483  
      */
 2484  
     public static boolean contains( @Nullable String str, @Nullable String searchStr )
 2485  
     {
 2486  10
         return !( str == null || searchStr == null ) && str.contains( searchStr );
 2487  
     }
 2488  
 }