View Javadoc

1   package org.apache.maven.shared.utils;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  
23  import java.util.Arrays;
24  import java.util.Iterator;
25  import java.util.Locale;
26  import java.util.Map;
27  import java.util.StringTokenizer;
28  
29  import javax.annotation.Nonnull;
30  import javax.annotation.Nullable;
31  
32  /**
33   * <p>Common <code>String</code> manipulation routines.</p>
34   * <p/>
35   * <p>Originally from
36   * <a href="http://jakarta.apache.org/turbine/">Turbine</a>, the
37   * GenerationJavaCore library and Velocity.
38   * Later a lots methods from commons-lang StringUtils
39   * got added too. Gradually smaller additions and fixes have been made
40   * over the time by various ASF committers.</p>
41   *
42   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
43   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
44   * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
45   * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
46   * @author <a href="mailto:ed@apache.org">Ed Korthof</a>
47   * @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a>
48   * @author Stephen Colebourne
49   * @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a>
50   * @author Holger Krauth
51   * @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
52   * @version $Id: StringUtils.java 1402496 2012-10-26 13:00:39Z rfscholte $
53   * 
54   */
55  @SuppressWarnings( "JavaDoc" )
56  public class StringUtils
57  {
58      /**
59       * <p><code>StringUtils</code> instances should NOT be constructed in
60       * standard programming. Instead, the class should be used as
61       * <code>StringUtils.trim(" foo ");</code>.</p>
62       * <p/>
63       * <p>This constructor is public to permit tools that require a JavaBean
64       * manager to operate.</p>
65       */
66      public StringUtils()
67      {
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.substring( start, end ) ).append( with );
776             start = end + repl.length();
777 
778             if ( --max == 0 )
779             {
780                 break;
781             }
782         }
783         buf.append( text.substring( start ) );
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 text.substring( 0, start ) + overlay + text.substring( end );
805     }
806 
807     // Centering
808     //--------------------------------------------------------------------------
809 
810     /**
811      * <p>Center a String in a larger String of size <code>n</code>.<p>
812      * <p/>
813      * <p>Uses spaces as the value to buffer the String with.
814      * Equivalent to <code>center(str, size, " ")</code>.</p>
815      *
816      * @param str  String to center
817      * @param size int size of new String
818      * @return String containing centered String
819      * @throws NullPointerException if str is <code>null</code>
820      */
821     public @Nonnull static String center( @Nonnull String str, int size )
822     {
823         return center( str, size, " " );
824     }
825 
826     /**
827      * <p>Center a String in a larger String of size <code>n</code>.</p>
828      * <p/>
829      * <p>Uses a supplied String as the value to buffer the String with.</p>
830      *
831      * @param str   String to center
832      * @param size  int size of new String
833      * @param delim String to buffer the new String with
834      * @return String containing centered String
835      * @throws NullPointerException if str or delim is <code>null</code>
836      * @throws ArithmeticException  if delim is the empty String
837      */
838     public @Nonnull static String center( @Nonnull String str, int size, @Nonnull String delim )
839     {
840         int sz = str.length();
841         int p = size - sz;
842         if ( p < 1 )
843         {
844             return str;
845         }
846         str = leftPad( str, sz + p / 2, delim );
847         str = rightPad( str, size, delim );
848         return str;
849     }
850 
851     // Chomping
852     //--------------------------------------------------------------------------
853 
854     /**
855      * <p>Remove the last newline, and everything after it from a String.</p>
856      *
857      * @param str String to chomp the newline from
858      * @return String without chomped newline
859      * @throws NullPointerException if str is <code>null</code>
860      */
861     public @Nonnull static String chomp( @Nonnull String str )
862     {
863         return chomp( str, "\n" );
864     }
865 
866     /**
867      * <p>Remove the last value of a supplied String, and everything after
868      * it from a String.</p>
869      *
870      * @param str String to chomp from
871      * @param sep String to chomp
872      * @return String without chomped ending
873      * @throws NullPointerException if str or sep is <code>null</code>
874      */
875     public @Nonnull static String chomp( @Nonnull String str, @Nonnull String sep )
876     {
877         int idx = str.lastIndexOf( sep );
878         if ( idx != -1 )
879         {
880             return str.substring( 0, idx );
881         }
882         else
883         {
884             return str;
885         }
886     }
887 
888     /**
889      * <p>Remove a newline if and only if it is at the end
890      * of the supplied String.</p>
891      *
892      * @param str String to chomp from
893      * @return String without chomped ending
894      * @throws NullPointerException if str is <code>null</code>
895      */
896     public @Nonnull static String chompLast( @Nonnull String str )
897     {
898         return chompLast( str, "\n" );
899     }
900 
901     /**
902      * <p>Remove a value if and only if the String ends with that value.</p>
903      *
904      * @param str String to chomp from
905      * @param sep String to chomp
906      * @return String without chomped ending
907      * @throws NullPointerException if str or sep is <code>null</code>
908      */
909     public @Nonnull static String chompLast( @Nonnull String str, @Nonnull String sep )
910     {
911         if ( str.length() == 0 )
912         {
913             return str;
914         }
915         String sub = str.substring( str.length() - sep.length() );
916         if ( sep.equals( sub ) )
917         {
918             return str.substring( 0, str.length() - sep.length() );
919         }
920         else
921         {
922             return str;
923         }
924     }
925 
926     /**
927      * <p>Remove everything and return the last value of a supplied String, and
928      * everything after it from a String.</p>
929      *
930      * @param str String to chomp from
931      * @param sep String to chomp
932      * @return String chomped
933      * @throws NullPointerException if str or sep is <code>null</code>
934      */
935     public @Nonnull static String getChomp( @Nonnull String str, @Nonnull String sep )
936     {
937         int idx = str.lastIndexOf( sep );
938         if ( idx == str.length() - sep.length() )
939         {
940             return sep;
941         }
942         else if ( idx != -1 )
943         {
944             return str.substring( idx );
945         }
946         else
947         {
948             return "";
949         }
950     }
951 
952     /**
953      * <p>Remove the first value of a supplied String, and everything before it
954      * from a String.</p>
955      *
956      * @param str String to chomp from
957      * @param sep String to chomp
958      * @return String without chomped beginning
959      * @throws NullPointerException if str or sep is <code>null</code>
960      */
961     public @Nonnull static String prechomp( @Nonnull String str, @Nonnull String sep )
962     {
963         int idx = str.indexOf( sep );
964         if ( idx != -1 )
965         {
966             return str.substring( idx + sep.length() );
967         }
968         else
969         {
970             return str;
971         }
972     }
973 
974     /**
975      * <p>Remove and return everything before the first value of a
976      * supplied String from another String.</p>
977      *
978      * @param str String to chomp from
979      * @param sep String to chomp
980      * @return String prechomped
981      * @throws NullPointerException if str or sep is <code>null</code>
982      */
983     public @Nonnull static String getPrechomp( @Nonnull String str, String sep )
984     {
985         int idx = str.indexOf( sep );
986         if ( idx != -1 )
987         {
988             return str.substring( 0, idx + sep.length() );
989         }
990         else
991         {
992             return "";
993         }
994     }
995 
996     // Chopping
997     //--------------------------------------------------------------------------
998 
999     /**
1000      * <p>Remove the last character from a String.</p>
1001      * <p/>
1002      * <p>If the String ends in <code>\r\n</code>, then remove both
1003      * of them.</p>
1004      *
1005      * @param str String to chop last character from
1006      * @return String without last character
1007      * @throws NullPointerException if str is <code>null</code>
1008      */
1009     public @Nonnull static String chop( @Nonnull String str )
1010     {
1011         if ( "".equals( str ) )
1012         {
1013             return "";
1014         }
1015         if ( str.length() == 1 )
1016         {
1017             return "";
1018         }
1019         int lastIdx = str.length() - 1;
1020         String ret = str.substring( 0, lastIdx );
1021         char last = str.charAt( lastIdx );
1022         if ( last == '\n' )
1023         {
1024             if ( ret.charAt( lastIdx - 1 ) == '\r' )
1025             {
1026                 return ret.substring( 0, lastIdx - 1 );
1027             }
1028         }
1029         return ret;
1030     }
1031 
1032     /**
1033      * <p>Remove <code>\n</code> from end of a String if it's there.
1034      * If a <code>\r</code> precedes it, then remove that too.</p>
1035      *
1036      * @param str String to chop a newline from
1037      * @return String without newline
1038      * @throws NullPointerException if str is <code>null</code>
1039      */
1040     public @Nonnull static String chopNewline( @Nonnull String str )
1041     {
1042         int lastIdx = str.length() - 1;
1043         char last = str.charAt( lastIdx );
1044         if ( last == '\n' )
1045         {
1046             if ( str.charAt( lastIdx - 1 ) == '\r' )
1047             {
1048                 lastIdx--;
1049             }
1050         }
1051         else
1052         {
1053             lastIdx++;
1054         }
1055         return str.substring( 0, lastIdx );
1056     }
1057 
1058     // Conversion
1059     //--------------------------------------------------------------------------
1060 
1061     // spec 3.10.6
1062 
1063     /**
1064      * <p>Escapes any values it finds into their String form.</p>
1065      * <p/>
1066      * <p>So a tab becomes the characters <code>'\\'</code> and
1067      * <code>'t'</code>.</p>
1068      *
1069      * @param str String to escape values in
1070      * @return String with escaped values
1071      * @throws NullPointerException if str is <code>null</code>
1072      */
1073     public @Nonnull static String escape( @Nonnull String str )
1074     {
1075         // improved with code from  cybertiger@cyberiantiger.org
1076         // unicode from him, and defaul for < 32's.
1077         int sz = str.length();
1078         StringBuilder buffer = new StringBuilder( 2 * sz );
1079         for ( int i = 0; i < sz; i++ )
1080         {
1081             char ch = str.charAt( i );
1082 
1083             // handle unicode
1084             if ( ch > 0xfff )
1085             {
1086                 buffer.append( "\\u" ).append( Integer.toHexString( ch ) );
1087             }
1088             else if ( ch > 0xff )
1089             {
1090                 buffer.append( "\\u0" ).append( Integer.toHexString( ch ) );
1091             }
1092             else if ( ch > 0x7f )
1093             {
1094                 buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
1095             }
1096             else if ( ch < 32 )
1097             {
1098                 switch ( ch )
1099                 {
1100                     case '\b':
1101                         buffer.append( '\\' );
1102                         buffer.append( 'b' );
1103                         break;
1104                     case '\n':
1105                         buffer.append( '\\' );
1106                         buffer.append( 'n' );
1107                         break;
1108                     case '\t':
1109                         buffer.append( '\\' );
1110                         buffer.append( 't' );
1111                         break;
1112                     case '\f':
1113                         buffer.append( '\\' );
1114                         buffer.append( 'f' );
1115                         break;
1116                     case '\r':
1117                         buffer.append( '\\' );
1118                         buffer.append( 'r' );
1119                         break;
1120                     default:
1121                         if ( ch > 0xf )
1122                         {
1123                             buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
1124                         }
1125                         else
1126                         {
1127                             buffer.append( "\\u000" ).append( Integer.toHexString( ch ) );
1128                         }
1129                         break;
1130                 }
1131             }
1132             else
1133             {
1134                 switch ( ch )
1135                 {
1136                     case '\'':
1137                         buffer.append( '\\' );
1138                         buffer.append( '\'' );
1139                         break;
1140                     case '"':
1141                         buffer.append( '\\' );
1142                         buffer.append( '"' );
1143                         break;
1144                     case '\\':
1145                         buffer.append( '\\' );
1146                         buffer.append( '\\' );
1147                         break;
1148                     default:
1149                         buffer.append( ch );
1150                         break;
1151                 }
1152             }
1153         }
1154         return buffer.toString();
1155     }
1156 
1157     // Padding
1158     //--------------------------------------------------------------------------
1159 
1160     /**
1161      * <p>Repeat a String <code>n</code> times to form a
1162      * new string.</p>
1163      *
1164      * @param str    String to repeat
1165      * @param repeat number of times to repeat str
1166      * @return String with repeated String
1167      * @throws NegativeArraySizeException if <code>repeat < 0</code>
1168      * @throws NullPointerException       if str is <code>null</code>
1169      */
1170     public @Nonnull static String repeat( @Nonnull String str, int repeat )
1171     {
1172         StringBuilder buffer = new StringBuilder( repeat * str.length() );
1173         for ( int i = 0; i < repeat; i++ )
1174         {
1175             buffer.append( str );
1176         }
1177         return buffer.toString();
1178     }
1179 
1180     /**
1181      * <p>Right pad a String with spaces.</p>
1182      * <p/>
1183      * <p>The String is padded to the size of <code>n</code>.</p>
1184      *
1185      * @param str  String to repeat
1186      * @param size number of times to repeat str
1187      * @return right padded String
1188      * @throws NullPointerException if str is <code>null</code>
1189      */
1190     public @Nonnull static String rightPad( @Nonnull String str, int size )
1191     {
1192         return rightPad( str, size, " " );
1193     }
1194 
1195     /**
1196      * <p>Right pad a String with a specified string.</p>
1197      * <p/>
1198      * <p>The String is padded to the size of <code>n</code>.</p>
1199      *
1200      * @param str   String to pad out
1201      * @param size  size to pad to
1202      * @param delim String to pad with
1203      * @return right padded String
1204      * @throws NullPointerException if str or delim is <code>null</code>
1205      * @throws ArithmeticException  if delim is the empty String
1206      */
1207     public @Nonnull static String rightPad( @Nonnull String str, int size, @Nonnull String delim )
1208     {
1209         size = ( size - str.length() ) / delim.length();
1210         if ( size > 0 )
1211         {
1212             str += repeat( delim, size );
1213         }
1214         return str;
1215     }
1216 
1217     /**
1218      * <p>Left pad a String with spaces.</p>
1219      * <p/>
1220      * <p>The String is padded to the size of <code>n</code>.</p>
1221      *
1222      * @param str  String to pad out
1223      * @param size size to pad to
1224      * @return left padded String
1225      * @throws NullPointerException if str or delim is <code>null</code>
1226      */
1227     public @Nonnull static String leftPad( @Nonnull String str, int size )
1228     {
1229         return leftPad( str, size, " " );
1230     }
1231 
1232     /**
1233      * Left pad a String with a specified string. Pad to a size of n.
1234      *
1235      * @param str   String to pad out
1236      * @param size  size to pad to
1237      * @param delim String to pad with
1238      * @return left padded String
1239      * @throws NullPointerException if str or delim is null
1240      * @throws ArithmeticException  if delim is the empty string
1241      */
1242     public @Nonnull static String leftPad( @Nonnull String str, int size, @Nonnull String delim )
1243     {
1244         size = ( size - str.length() ) / delim.length();
1245         if ( size > 0 )
1246         {
1247             str = repeat( delim, size ) + str;
1248         }
1249         return str;
1250     }
1251 
1252     // Stripping
1253     //--------------------------------------------------------------------------
1254 
1255     /**
1256      * <p>Remove whitespace from the front and back of a String.</p>
1257      *
1258      * @param str the String to remove whitespace from
1259      * @return the stripped String
1260      */
1261     public static String strip( String str )
1262     {
1263         return strip( str, null );
1264     }
1265 
1266     /**
1267      * <p>Remove a specified String from the front and back of a
1268      * String.</p>
1269      * <p/>
1270      * <p>If whitespace is wanted to be removed, used the
1271      * {@link #strip(java.lang.String)} method.</p>
1272      *
1273      * @param str   the String to remove a string from
1274      * @param delim the String to remove at start and end
1275      * @return the stripped String
1276      */
1277     public static String strip( String str, @Nullable String delim )
1278     {
1279         str = stripStart( str, delim );
1280         return stripEnd( str, delim );
1281     }
1282 
1283     /**
1284      * <p>Strip whitespace from the front and back of every String
1285      * in the array.</p>
1286      *
1287      * @param strs the Strings to remove whitespace from
1288      * @return the stripped Strings
1289      */
1290     public static String[] stripAll( String... strs )
1291     {
1292         return stripAll( strs, null );
1293     }
1294 
1295     /**
1296      * <p>Strip the specified delimiter from the front and back of
1297      * every String in the array.</p>
1298      *
1299      * @param strs      the Strings to remove a String from
1300      * @param delimiter the String to remove at start and end
1301      * @return the stripped Strings
1302      */
1303     public static String[] stripAll( String[] strs, @Nullable String delimiter )
1304     {
1305         if ( ( strs == null ) || ( strs.length == 0 ) )
1306         {
1307             return strs;
1308         }
1309         int sz = strs.length;
1310         String[] newArr = new String[sz];
1311         for ( int i = 0; i < sz; i++ )
1312         {
1313             newArr[i] = strip( strs[i], delimiter );
1314         }
1315         return newArr;
1316     }
1317 
1318     /**
1319      * <p>Strip any of a supplied String from the end of a String.</p>
1320      * <p/>
1321      * <p>If the strip String is <code>null</code>, whitespace is
1322      * stripped.</p>
1323      *
1324      * @param str   the String to remove characters from
1325      * @param strip the String to remove
1326      * @return the stripped String
1327      */
1328     public static String stripEnd( String str, @Nullable String strip )
1329     {
1330         if ( str == null )
1331         {
1332             return null;
1333         }
1334         int end = str.length();
1335 
1336         if ( strip == null )
1337         {
1338             while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) )
1339             {
1340                 end--;
1341             }
1342         }
1343         else
1344         {
1345             while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) )
1346             {
1347                 end--;
1348             }
1349         }
1350         return str.substring( 0, end );
1351     }
1352 
1353     /**
1354      * <p>Strip any of a supplied String from the start of a String.</p>
1355      * <p/>
1356      * <p>If the strip String is <code>null</code>, whitespace is
1357      * stripped.</p>
1358      *
1359      * @param str   the String to remove characters from
1360      * @param strip the String to remove
1361      * @return the stripped String
1362      */
1363     public static String stripStart( String str, @Nullable String strip )
1364     {
1365         if ( str == null )
1366         {
1367             return null;
1368         }
1369 
1370         int start = 0;
1371 
1372         int sz = str.length();
1373 
1374         if ( strip == null )
1375         {
1376             while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) )
1377             {
1378                 start++;
1379             }
1380         }
1381         else
1382         {
1383             while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) )
1384             {
1385                 start++;
1386             }
1387         }
1388         return str.substring( start );
1389     }
1390 
1391     // Case conversion
1392     //--------------------------------------------------------------------------
1393 
1394     /**
1395      * <p>Convert a String to upper case, <code>null</code> String
1396      * returns <code>null</code>.</p>
1397      *
1398      * @param str the String to uppercase
1399      * @return the upper cased String
1400      */
1401     public static String upperCase( String str )
1402     {
1403         if ( str == null )
1404         {
1405             return null;
1406         }
1407         return str.toUpperCase();
1408     }
1409 
1410     /**
1411      * <p>Convert a String to lower case, <code>null</code> String
1412      * returns <code>null</code>.</p>
1413      *
1414      * @param str the string to lowercase
1415      * @return the lower cased String
1416      */
1417     public static String lowerCase( String str )
1418     {
1419         if ( str == null )
1420         {
1421             return null;
1422         }
1423         return str.toLowerCase();
1424     }
1425 
1426     /**
1427      * <p>Uncapitalise a String.</p>
1428      * <p/>
1429      * <p>That is, convert the first character into lower-case.
1430      * <code>null</code> is returned as <code>null</code>.</p>
1431      *
1432      * @param str the String to uncapitalise
1433      * @return uncapitalised String
1434      */
1435     public static String uncapitalise( String str )
1436     {
1437         if ( str == null )
1438         {
1439             return null;
1440         }
1441         else if ( str.length() == 0 )
1442         {
1443             return "";
1444         }
1445         else
1446         {
1447             return String.valueOf( Character.toLowerCase( str.charAt( 0 ) ) ) + str.substring( 1 );
1448         }
1449     }
1450 
1451     /**
1452      * <p>Capitalise a String.</p>
1453      * <p/>
1454      * <p>That is, convert the first character into title-case.
1455      * <code>null</code> is returned as <code>null</code>.</p>
1456      *
1457      * @param str the String to capitalise
1458      * @return capitalised String
1459      */
1460     public static String capitalise( String str )
1461     {
1462         if ( str == null )
1463         {
1464             return null;
1465         }
1466         else if ( str.length() == 0 )
1467         {
1468             return "";
1469         }
1470         else
1471         {
1472             return String.valueOf( Character.toTitleCase( str.charAt( 0 ) ) ) + str.substring( 1 );
1473         }
1474     }
1475 
1476     /**
1477      * <p>Swaps the case of String.</p>
1478      * <p/>
1479      * <p>Properly looks after making sure the start of words
1480      * are Titlecase and not Uppercase.</p>
1481      * <p/>
1482      * <p><code>null</code> is returned as <code>null</code>.</p>
1483      *
1484      * @param str the String to swap the case of
1485      * @return the modified String
1486      */
1487     public static String swapCase( String str )
1488     {
1489         if ( str == null )
1490         {
1491             return null;
1492         }
1493         int sz = str.length();
1494         StringBuilder buffer = new StringBuilder( sz );
1495 
1496         boolean whitespace = false;
1497         char ch;
1498         char tmp;
1499 
1500         for ( int i = 0; i < sz; i++ )
1501         {
1502             ch = str.charAt( i );
1503             if ( Character.isUpperCase( ch ) )
1504             {
1505                 tmp = Character.toLowerCase( ch );
1506             }
1507             else if ( Character.isTitleCase( ch ) )
1508             {
1509                 tmp = Character.toLowerCase( ch );
1510             }
1511             else if ( Character.isLowerCase( ch ) )
1512             {
1513                 if ( whitespace )
1514                 {
1515                     tmp = Character.toTitleCase( ch );
1516                 }
1517                 else
1518                 {
1519                     tmp = Character.toUpperCase( ch );
1520                 }
1521             }
1522             else
1523             {
1524                 tmp = ch;
1525             }
1526             buffer.append( tmp );
1527             whitespace = Character.isWhitespace( ch );
1528         }
1529         return buffer.toString();
1530     }
1531 
1532 
1533     /**
1534      * <p>Capitalise all the words in a String.</p>
1535      * <p/>
1536      * <p>Uses {@link Character#isWhitespace(char)} as a
1537      * separator between words.</p>
1538      * <p/>
1539      * <p><code>null</code> will return <code>null</code>.</p>
1540      *
1541      * @param str the String to capitalise
1542      * @return capitalised String
1543      */
1544     public static String capitaliseAllWords( String str )
1545     {
1546         if ( str == null )
1547         {
1548             return null;
1549         }
1550         int sz = str.length();
1551         StringBuilder buffer = new StringBuilder( sz );
1552         boolean space = true;
1553         for ( int i = 0; i < sz; i++ )
1554         {
1555             char ch = str.charAt( i );
1556             if ( Character.isWhitespace( ch ) )
1557             {
1558                 buffer.append( ch );
1559                 space = true;
1560             }
1561             else if ( space )
1562             {
1563                 buffer.append( Character.toTitleCase( ch ) );
1564                 space = false;
1565             }
1566             else
1567             {
1568                 buffer.append( ch );
1569             }
1570         }
1571         return buffer.toString();
1572     }
1573 
1574     /**
1575      * <p>Uncapitalise all the words in a string.</p>
1576      * <p/>
1577      * <p>Uses {@link Character#isWhitespace(char)} as a
1578      * separator between words.</p>
1579      * <p/>
1580      * <p><code>null</code> will return <code>null</code>.</p>
1581      *
1582      * @param str the string to uncapitalise
1583      * @return uncapitalised string
1584      */
1585     public static String uncapitaliseAllWords( String str )
1586     {
1587         if ( str == null )
1588         {
1589             return null;
1590         }
1591         int sz = str.length();
1592         StringBuilder buffer = new StringBuilder( sz );
1593         boolean space = true;
1594         for ( int i = 0; i < sz; i++ )
1595         {
1596             char ch = str.charAt( i );
1597             if ( Character.isWhitespace( ch ) )
1598             {
1599                 buffer.append( ch );
1600                 space = true;
1601             }
1602             else if ( space )
1603             {
1604                 buffer.append( Character.toLowerCase( ch ) );
1605                 space = false;
1606             }
1607             else
1608             {
1609                 buffer.append( ch );
1610             }
1611         }
1612         return buffer.toString();
1613     }
1614 
1615     // Nested extraction
1616     //--------------------------------------------------------------------------
1617 
1618     /**
1619      * <p>Get the String that is nested in between two instances of the
1620      * same String.</p>
1621      * <p/>
1622      * <p>If <code>str</code> is <code>null</code>, will
1623      * return <code>null</code>.</p>
1624      *
1625      * @param str the String containing nested-string
1626      * @param tag the String before and after nested-string
1627      * @return the String that was nested, or <code>null</code>
1628      * @throws NullPointerException if tag is <code>null</code>
1629      */
1630     public static String getNestedString( String str, @Nonnull String tag )
1631     {
1632         return getNestedString( str, tag, tag );
1633     }
1634 
1635     /**
1636      * <p>Get the String that is nested in between two Strings.</p>
1637      *
1638      * @param str   the String containing nested-string
1639      * @param open  the String before nested-string
1640      * @param close the String after nested-string
1641      * @return the String that was nested, or <code>null</code>
1642      * @throws NullPointerException if open or close is <code>null</code>
1643      */
1644     public static String getNestedString( String str, @Nonnull String open, @Nonnull String close )
1645     {
1646         if ( str == null )
1647         {
1648             return null;
1649         }
1650         int start = str.indexOf( open );
1651         if ( start != -1 )
1652         {
1653             int end = str.indexOf( close, start + open.length() );
1654             if ( end != -1 )
1655             {
1656                 return str.substring( start + open.length(), end );
1657             }
1658         }
1659         return null;
1660     }
1661 
1662     /**
1663      * <p>How many times is the substring in the larger String.</p>
1664      * <p/>
1665      * <p><code>null</code> returns <code>0</code>.</p>
1666      *
1667      * @param str the String to check
1668      * @param sub the substring to count
1669      * @return the number of occurances, 0 if the String is <code>null</code>
1670      * @throws NullPointerException if sub is <code>null</code>
1671      */
1672     public static int countMatches( @Nullable String str, @Nonnull String sub )
1673     {
1674         if ( sub.equals( "" ) )
1675         {
1676             return 0;
1677         }
1678         if ( str == null )
1679         {
1680             return 0;
1681         }
1682         int count = 0;
1683         int idx = 0;
1684         while ( ( idx = str.indexOf( sub, idx ) ) != -1 )
1685         {
1686             count++;
1687             idx += sub.length();
1688         }
1689         return count;
1690     }
1691 
1692     // Character Tests
1693     //--------------------------------------------------------------------------
1694 
1695     /**
1696      * <p>Checks if the String contains only unicode letters.</p>
1697      * <p/>
1698      * <p><code>null</code> will return <code>false</code>.
1699      * An empty String will return <code>true</code>.</p>
1700      *
1701      * @param str the String to check
1702      * @return <code>true</code> if only contains letters, and is non-null
1703      */
1704     public static boolean isAlpha( String str )
1705     {
1706         if ( str == null )
1707         {
1708             return false;
1709         }
1710         int sz = str.length();
1711         for ( int i = 0; i < sz; i++ )
1712         {
1713             if ( !Character.isLetter( str.charAt( i ) ) )
1714             {
1715                 return false;
1716             }
1717         }
1718         return true;
1719     }
1720 
1721     /**
1722      * <p>Checks if the String contains only whitespace.</p>
1723      * <p/>
1724      * <p><code>null</code> will return <code>false</code>. An
1725      * empty String will return <code>true</code>.</p>
1726      *
1727      * @param str the String to check
1728      * @return <code>true</code> if only contains whitespace, and is non-null
1729      */
1730     public static boolean isWhitespace( String str )
1731     {
1732         if ( str == null )
1733         {
1734             return false;
1735         }
1736         int sz = str.length();
1737         for ( int i = 0; i < sz; i++ )
1738         {
1739             if ( ( !Character.isWhitespace( str.charAt( i ) ) ) )
1740             {
1741                 return false;
1742             }
1743         }
1744         return true;
1745     }
1746 
1747     /**
1748      * <p>Checks if the String contains only unicode letters and
1749      * space (<code>' '</code>).</p>
1750      * <p/>
1751      * <p><code>null</code> will return <code>false</code>. An
1752      * empty String will return <code>true</code>.</p>
1753      *
1754      * @param str the String to check
1755      * @return <code>true</code> if only contains letters and space,
1756      *         and is non-null
1757      */
1758     public static boolean isAlphaSpace( String str )
1759     {
1760         if ( str == null )
1761         {
1762             return false;
1763         }
1764         int sz = str.length();
1765         for ( int i = 0; i < sz; i++ )
1766         {
1767             if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
1768             {
1769                 return false;
1770             }
1771         }
1772         return true;
1773     }
1774 
1775     /**
1776      * <p>Checks if the String contains only unicode letters or digits.</p>
1777      * <p/>
1778      * <p><code>null</code> will return <code>false</code>. An empty
1779      * String will return <code>true</code>.</p>
1780      *
1781      * @param str the String to check
1782      * @return <code>true</code> if only contains letters or digits,
1783      *         and is non-null
1784      */
1785     public static boolean isAlphanumeric( String str )
1786     {
1787         if ( str == null )
1788         {
1789             return false;
1790         }
1791         int sz = str.length();
1792         for ( int i = 0; i < sz; i++ )
1793         {
1794             if ( !Character.isLetterOrDigit( str.charAt( i ) ) )
1795             {
1796                 return false;
1797             }
1798         }
1799         return true;
1800     }
1801 
1802     /**
1803      * <p>Checks if the String contains only unicode letters, digits
1804      * or space (<code>' '</code>).</p>
1805      * <p/>
1806      * <p><code>null</code> will return <code>false</code>. An empty
1807      * String will return <code>true</code>.</p>
1808      *
1809      * @param str the String to check
1810      * @return <code>true</code> if only contains letters, digits or space,
1811      *         and is non-null
1812      */
1813     public static boolean isAlphanumericSpace( String str )
1814     {
1815         if ( str == null )
1816         {
1817             return false;
1818         }
1819         int sz = str.length();
1820         for ( int i = 0; i < sz; i++ )
1821         {
1822             if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
1823             {
1824                 return false;
1825             }
1826         }
1827         return true;
1828     }
1829 
1830     /**
1831      * <p>Checks if the String contains only unicode digits.</p>
1832      * <p/>
1833      * <p><code>null</code> will return <code>false</code>.
1834      * An empty String will return <code>true</code>.</p>
1835      *
1836      * @param str the String to check
1837      * @return <code>true</code> if only contains digits, and is non-null
1838      */
1839     public static boolean isNumeric( String str )
1840     {
1841         if ( str == null )
1842         {
1843             return false;
1844         }
1845         int sz = str.length();
1846         for ( int i = 0; i < sz; i++ )
1847         {
1848             if ( !Character.isDigit( str.charAt( i ) ) )
1849             {
1850                 return false;
1851             }
1852         }
1853         return true;
1854     }
1855 
1856     // Defaults
1857     //--------------------------------------------------------------------------
1858 
1859     /**
1860      * <p>Returns either the passed in <code>Object</code> as a String,
1861      * or, if the <code>Object</code> is <code>null</code>, an empty
1862      * String.</p>
1863      *
1864      * @param obj the Object to check
1865      * @return the passed in Object's toString, or blank if it was
1866      *         <code>null</code>
1867      */
1868     public @Nonnull static String defaultString( Object obj )
1869     {
1870         return defaultString( obj, "" );
1871     }
1872 
1873     /**
1874      * <p>Returns either the passed in <code>Object</code> as a String,
1875      * or, if the <code>Object</code> is <code>null</code>, a passed
1876      * in default String.</p>
1877      *
1878      * @param obj           the Object to check
1879      * @param defaultString the default String to return if str is
1880      *                      <code>null</code>
1881      * @return the passed in string, or the default if it was
1882      *         <code>null</code>
1883      */
1884     public @Nonnull static String defaultString( Object obj, @Nonnull String defaultString )
1885     {
1886         return ( obj == null ) ? defaultString : obj.toString();
1887     }
1888 
1889     // Reversing
1890     //--------------------------------------------------------------------------
1891 
1892     /**
1893      * <p>Reverse a String.</p>
1894      * <p/>
1895      * <p><code>null</code> String returns <code>null</code>.</p>
1896      *
1897      * @param str the String to reverse
1898      * @return the reversed String
1899      */
1900     public static String reverse( String str )
1901     {
1902         if ( str == null )
1903         {
1904             return null;
1905         }
1906         return new StringBuffer( str ).reverse().toString();
1907     }
1908 
1909     /**
1910      * <p>Reverses a String that is delimited by a specific character.</p>
1911      * <p/>
1912      * <p>The Strings between the delimiters are not reversed.
1913      * Thus java.lang.String becomes String.lang.java (if the delimiter
1914      * is <code>'.'</code>).</p>
1915      *
1916      * @param str       the String to reverse
1917      * @param delimiter the delimiter to use
1918      * @return the reversed String
1919      */
1920     public @Nonnull static String reverseDelimitedString( @Nonnull String str, String delimiter )
1921     {
1922         // could implement manually, but simple way is to reuse other,
1923         // probably slower, methods.
1924         String[] strs = split( str, delimiter );
1925         reverseArray( strs );
1926         return join( strs, delimiter );
1927     }
1928 
1929     /**
1930      * <p>Reverses an array.</p>
1931      * <p/>
1932      * <p>TAKEN FROM CollectionsUtils.</p>
1933      *
1934      * @param array the array to reverse
1935      */
1936     private static void reverseArray( @Nonnull String... array )
1937     {
1938         int i = 0;
1939         int j = array.length - 1;
1940         String tmp;
1941 
1942         while ( j > i )
1943         {
1944             tmp = array[j];
1945             array[j] = array[i];
1946             array[i] = tmp;
1947             j--;
1948             i++;
1949         }
1950     }
1951 
1952     // Abbreviating
1953     //--------------------------------------------------------------------------
1954 
1955     /**
1956      * Turn "Now is the time for all good men" into "Now is the time for..."
1957      * <p/>
1958      * Specifically:
1959      * <p/>
1960      * If str is less than max characters long, return it.
1961      * Else abbreviate it to (substring(str, 0, max-3) + "...").
1962      * If maxWidth is less than 3, throw an IllegalArgumentException.
1963      * In no case will it return a string of length greater than maxWidth.
1964      *
1965      * @param maxWidth maximum length of result string
1966      */
1967     public @Nonnull static String abbreviate( @Nonnull String s, int maxWidth )
1968     {
1969         return abbreviate( s, 0, maxWidth );
1970     }
1971 
1972     /**
1973      * Turn "Now is the time for all good men" into "...is the time for..."
1974      * <p/>
1975      * Works like abbreviate(String, int), but allows you to specify a "left edge"
1976      * offset.  Note that this left edge is not necessarily going to be the leftmost
1977      * character in the result, or the first
1978      * character following the ellipses, but it will appear somewhere in the result.
1979      * In no case will it return a string of length greater than maxWidth.
1980      *
1981      * @param offset   left edge of source string
1982      * @param maxWidth maximum length of result string
1983      */
1984     public @Nonnull static String abbreviate( @Nonnull String s, int offset, int maxWidth )
1985     {
1986         if ( maxWidth < 4 )
1987         {
1988             throw new IllegalArgumentException( "Minimum abbreviation width is 4" );
1989         }
1990         if ( s.length() <= maxWidth )
1991         {
1992             return s;
1993         }
1994         if ( offset > s.length() )
1995         {
1996             offset = s.length();
1997         }
1998         if ( ( s.length() - offset ) < ( maxWidth - 3 ) )
1999         {
2000             offset = s.length() - ( maxWidth - 3 );
2001         }
2002         if ( offset <= 4 )
2003         {
2004             return s.substring( 0, maxWidth - 3 ) + "...";
2005         }
2006         if ( maxWidth < 7 )
2007         {
2008             throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" );
2009         }
2010         if ( ( offset + ( maxWidth - 3 ) ) < s.length() )
2011         {
2012             return "..." + abbreviate( s.substring( offset ), maxWidth - 3 );
2013         }
2014         return "..." + s.substring( s.length() - ( maxWidth - 3 ) );
2015     }
2016 
2017     // Difference
2018     //--------------------------------------------------------------------------
2019 
2020     /**
2021      * Compare two strings, and return the portion where they differ.
2022      * (More precisely, return the remainder of the second string,
2023      * starting from where it's different from the first.)
2024      * <p/>
2025      * E.g. strdiff("i am a machine", "i am a robot") -> "robot"
2026      *
2027      * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal
2028      */
2029     public static String difference( @Nonnull String s1, @Nonnull String s2 )
2030     {
2031         int at = differenceAt( s1, s2 );
2032         if ( at == -1 )
2033         {
2034             return "";
2035         }
2036         return s2.substring( at );
2037     }
2038 
2039     /**
2040      * Compare two strings, and return the index at which the strings begin to differ.
2041      * <p>
2042      * E.g. strdiff("i am a machine", "i am a robot") -> 7
2043      * </p>
2044      *
2045      * @return the index where s2 and s1 begin to differ; -1 if they are equal
2046      */
2047     public static int differenceAt( @Nonnull String s1, @Nonnull String s2 )
2048     {
2049         int i;
2050         for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i )
2051         {
2052             if ( s1.charAt( i ) != s2.charAt( i ) )
2053             {
2054                 break;
2055             }
2056         }
2057         if ( ( i < s2.length() ) || ( i < s1.length() ) )
2058         {
2059             return i;
2060         }
2061         return -1;
2062     }
2063 
2064     /**
2065      * Fill all 'variables' in the given text with the values from the map.
2066      * Any text looking like '${key}' will get replaced by the value stored
2067      * in the namespace map under the 'key'.
2068      *
2069      * @param text
2070      * @param namespace
2071      * @return the interpolated text.
2072      */
2073     public static String interpolate( String text, @Nonnull Map<?, ?> namespace )
2074     {
2075         for ( Map.Entry<?, ?> entry : namespace.entrySet() )
2076         {
2077             String key = entry.getKey().toString();
2078 
2079             Object obj = entry.getValue();
2080 
2081             if ( obj == null )
2082             {
2083                 throw new NullPointerException( "The value of the key '" + key + "' is null." );
2084             }
2085 
2086             String value = obj.toString();
2087 
2088             text = replace( text, "${" + key + "}", value );
2089 
2090             if ( !key.contains( " " ) )
2091             {
2092                 text = replace( text, "$" + key, value );
2093             }
2094         }
2095         return text;
2096     }
2097 
2098     /**
2099      * This is basically the inverse of {@link #addAndDeHump(String)}.
2100      * It will remove the 'replaceThis' parameter and uppercase the next
2101      * character afterwards.
2102      * <pre>
2103      * removeAndHump( &quot;this-is-it&quot;, %quot;-&quot; );
2104      * </pre>
2105      * will become 'ThisIsIt'.
2106      *
2107      * @param data
2108      * @param replaceThis
2109      * @return humped String
2110      */
2111     public @Nonnull static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis )
2112     {
2113         String temp;
2114 
2115         StringBuilder out = new StringBuilder();
2116 
2117         temp = data;
2118 
2119         StringTokenizer st = new StringTokenizer( temp, replaceThis );
2120 
2121         while ( st.hasMoreTokens() )
2122         {
2123             String element = st.nextToken();
2124 
2125             out.append( capitalizeFirstLetter( element ) );
2126         }
2127 
2128         return out.toString();
2129     }
2130 
2131     /**
2132      * Convert the first character of the given String to uppercase.
2133      * This method will <i>not</i> trim of spaces!
2134      * <p/>
2135      * <p>
2136      * <b>Attention:</b> this method will currently throw a
2137      * <code>IndexOutOfBoundsException</code> for empty strings!
2138      * </p>
2139      *
2140      * @param data the String to get capitalized
2141      * @return data string with the first character transformed to uppercase
2142      * @throws NullPointerException if data is <code>null</code>
2143      */
2144     public @Nonnull static String capitalizeFirstLetter( @Nonnull String data )
2145     {
2146         char firstLetter = Character.toTitleCase( data.substring( 0, 1 ).charAt( 0 ) );
2147 
2148         String restLetters = data.substring( 1 );
2149 
2150         return firstLetter + restLetters;
2151     }
2152 
2153     /**
2154      * Convert the first character of the given String to lowercase.
2155      * This method will <i>not</i> trim of spaces!
2156      * <p/>
2157      * <p>
2158      * <b>Attention:</b> this method will currently throw a
2159      * <code>IndexOutOfBoundsException</code> for empty strings!
2160      * </p>
2161      *
2162      * @param data the String to get it's first character lower-cased.
2163      * @return data string with the first character transformed to lowercase
2164      * @throws NullPointerException if data is <code>null</code>
2165      */
2166     public @Nonnull static String lowercaseFirstLetter( @Nonnull String data )
2167     {
2168         char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) );
2169 
2170         String restLetters = data.substring( 1 );
2171 
2172         return firstLetter + restLetters;
2173     }
2174 
2175     /**
2176      * Take the input string and un-camel-case it.
2177      * <p/>
2178      * 'ThisIsIt' will become 'this-is-it'.
2179      *
2180      * @param view
2181      * @return deHumped String
2182      */
2183     public @Nonnull static String addAndDeHump( @Nonnull String view )
2184     {
2185         StringBuilder sb = new StringBuilder();
2186 
2187         for ( int i = 0; i < view.length(); i++ )
2188         {
2189             if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) )
2190             {
2191                 sb.append( '-' );
2192             }
2193 
2194             sb.append( view.charAt( i ) );
2195         }
2196 
2197         return sb.toString().trim().toLowerCase( Locale.ENGLISH );
2198     }
2199 
2200     /**
2201      * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
2202      * <p/>
2203      * <pre>
2204      * StringUtils.quoteAndEscape(null, *)    = null
2205      * StringUtils.quoteAndEscape("", *)      = ""
2206      * StringUtils.quoteAndEscape("abc", '"') = abc
2207      * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
2208      * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
2209      * </pre>
2210      *
2211      * @param source
2212      * @param quoteChar
2213      * @return the String quoted and escaped
2214      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2215      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2216      * 
2217      */
2218     public static String quoteAndEscape( @Nullable String source, char quoteChar )
2219     {
2220         return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false );
2221     }
2222 
2223     /**
2224      * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
2225      *
2226      * @param source
2227      * @param quoteChar
2228      * @param quotingTriggers
2229      * @return the String quoted and escaped
2230      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2231      * 
2232      */
2233     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers )
2234     {
2235         return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false );
2236     }
2237 
2238     /**
2239      * @param source
2240      * @param quoteChar
2241      * @param escapedChars
2242      * @param escapeChar
2243      * @param force
2244      * @return the String quoted and escaped
2245      * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2246      * 
2247      */
2248     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, char escapeChar,
2249                                          boolean force )
2250     {
2251         return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force );
2252     }
2253 
2254     /**
2255      * @param source
2256      * @param quoteChar
2257      * @param escapedChars
2258      * @param quotingTriggers
2259      * @param escapeChar
2260      * @param force
2261      * @return the String quoted and escaped
2262      * 
2263      */
2264     public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars,
2265                                          @Nonnull final char[] quotingTriggers, char escapeChar, boolean force )
2266     {
2267         if ( source == null )
2268         {
2269             return null;
2270         }
2271 
2272         if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith(
2273             Character.toString( quoteChar ) ) )
2274         {
2275             return source;
2276         }
2277 
2278         String escaped = escape( source, escapedChars, escapeChar );
2279 
2280         boolean quote = false;
2281         if ( force )
2282         {
2283             quote = true;
2284         }
2285         else if ( !escaped.equals( source ) )
2286         {
2287             quote = true;
2288         }
2289         else
2290         {
2291             for ( char quotingTrigger : quotingTriggers )
2292             {
2293                 if ( escaped.indexOf( quotingTrigger ) > -1 )
2294                 {
2295                     quote = true;
2296                     break;
2297                 }
2298             }
2299         }
2300 
2301         if ( quote )
2302         {
2303             return quoteChar + escaped + quoteChar;
2304         }
2305 
2306         return escaped;
2307     }
2308 
2309     /**
2310      * @param source
2311      * @param escapedChars
2312      * @param escapeChar
2313      * @return the String escaped
2314      * 
2315      */
2316     public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar )
2317     {
2318         if ( source == null )
2319         {
2320             return null;
2321         }
2322 
2323         char[] eqc = new char[escapedChars.length];
2324         System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length );
2325         Arrays.sort( eqc );
2326 
2327         StringBuilder buffer = new StringBuilder( source.length() );
2328 
2329         for ( int i = 0; i < source.length(); i++ )
2330         {
2331             final char c = source.charAt( i );
2332             int result = Arrays.binarySearch( eqc, c );
2333 
2334             if ( result > -1 )
2335             {
2336                 buffer.append( escapeChar );
2337             }
2338             buffer.append( c );
2339         }
2340 
2341         return buffer.toString();
2342     }
2343 
2344     /**
2345      * Remove all duplicate whitespace characters and line terminators are replaced with a single
2346      * space.
2347      *
2348      * @param s a not null String
2349      * @return a string with unique whitespace.
2350      * 
2351      */
2352     public @Nonnull static String removeDuplicateWhitespace( @Nonnull String s )
2353     {
2354         StringBuilder result = new StringBuilder();
2355         int length = s.length();
2356         boolean isPreviousWhiteSpace = false;
2357         for ( int i = 0; i < length; i++ )
2358         {
2359             char c = s.charAt( i );
2360             boolean thisCharWhiteSpace = Character.isWhitespace( c );
2361             if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) )
2362             {
2363                 result.append( c );
2364             }
2365             isPreviousWhiteSpace = thisCharWhiteSpace;
2366         }
2367         return result.toString();
2368     }
2369 
2370     /**
2371      * Parses the given String and replaces all occurrences of
2372      * '\n', '\r' and '\r\n' with the system line separator.
2373      *
2374      * @param s a not null String
2375      * @return a String that contains only System line separators.
2376      * @see #unifyLineSeparators(String, String)
2377      * 
2378      */
2379     public static String unifyLineSeparators( @Nullable String s )
2380     {
2381         return unifyLineSeparators( s, System.getProperty( "line.separator" ) );
2382     }
2383 
2384     /**
2385      * Parses the given String and replaces all occurrences of
2386      * '\n', '\r' and '\r\n' with the system line separator.
2387      *
2388      * @param s  a not null String
2389      * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator.
2390      * @return a String that contains only System line separators.
2391      * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters.
2392      * 
2393      */
2394     public static String unifyLineSeparators( @Nullable String s, @Nullable String ls )
2395     {
2396         if ( s == null )
2397         {
2398             return null;
2399         }
2400 
2401         if ( ls == null )
2402         {
2403             ls = System.getProperty( "line.separator" );
2404         }
2405 
2406         if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) )
2407         {
2408             throw new IllegalArgumentException( "Requested line separator is invalid." );
2409         }
2410 
2411         int length = s.length();
2412 
2413         StringBuilder buffer = new StringBuilder( length );
2414         for ( int i = 0; i < length; i++ )
2415         {
2416             if ( s.charAt( i ) == '\r' )
2417             {
2418                 if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' )
2419                 {
2420                     i++;
2421                 }
2422 
2423                 buffer.append( ls );
2424             }
2425             else if ( s.charAt( i ) == '\n' )
2426             {
2427                 buffer.append( ls );
2428             }
2429             else
2430             {
2431                 buffer.append( s.charAt( i ) );
2432             }
2433         }
2434 
2435         return buffer.toString();
2436     }
2437 
2438     /**
2439      * <p>Checks if String contains a search character, handling <code>null</code>.
2440      * This method uses {@link String#indexOf(int)}.</p>
2441      * <p/>
2442      * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p>
2443      * <p/>
2444      * <pre>
2445      * StringUtils.contains(null, *)    = false
2446      * StringUtils.contains("", *)      = false
2447      * StringUtils.contains("abc", 'a') = true
2448      * StringUtils.contains("abc", 'z') = false
2449      * </pre>
2450      *
2451      * @param str        the String to check, may be null
2452      * @param searchChar the character to find
2453      * @return true if the String contains the search character,
2454      *         false if not or <code>null</code> string input
2455      * 
2456      */
2457     @SuppressWarnings( "ConstantConditions" )
2458     public static boolean contains( @Nullable String str, char searchChar )
2459     {
2460         return !isEmpty( str ) && str.indexOf( searchChar ) >= 0;
2461     }
2462 
2463     /**
2464      * <p>Checks if String contains a search String, handling <code>null</code>.
2465      * This method uses {@link String#indexOf(int)}.</p>
2466      * <p/>
2467      * <p>A <code>null</code> String will return <code>false</code>.</p>
2468      * <p/>
2469      * <pre>
2470      * StringUtils.contains(null, *)     = false
2471      * StringUtils.contains(*, null)     = false
2472      * StringUtils.contains("", "")      = true
2473      * StringUtils.contains("abc", "")   = true
2474      * StringUtils.contains("abc", "a")  = true
2475      * StringUtils.contains("abc", "z")  = false
2476      * </pre>
2477      *
2478      * @param str       the String to check, may be null
2479      * @param searchStr the String to find, may be null
2480      * @return true if the String contains the search String,
2481      *         false if not or <code>null</code> string input
2482      * 
2483      */
2484     public static boolean contains( @Nullable String str, @Nullable String searchStr )
2485     {
2486         return !( str == null || searchStr == null ) && str.contains( searchStr );
2487     }
2488 }