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