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