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