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