001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.lang3;
018    
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    import java.util.ArrayList;
022    import java.util.Arrays;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Locale;
026    import java.util.regex.Pattern;
027    
028    /**
029     * <p>Operations on {@link java.lang.String} that are
030     * {@code null} safe.</p>
031     *
032     * <ul>
033     *  <li><b>IsEmpty/IsBlank</b>
034     *      - checks if a String contains text</li>
035     *  <li><b>Trim/Strip</b>
036     *      - removes leading and trailing whitespace</li>
037     *  <li><b>Equals</b>
038     *      - compares two strings null-safe</li>
039     *  <li><b>startsWith</b>
040     *      - check if a String starts with a prefix null-safe</li>
041     *  <li><b>endsWith</b>
042     *      - check if a String ends with a suffix null-safe</li>
043     *  <li><b>IndexOf/LastIndexOf/Contains</b>
044     *      - null-safe index-of checks
045     *  <li><b>IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut</b>
046     *      - index-of any of a set of Strings</li>
047     *  <li><b>ContainsOnly/ContainsNone/ContainsAny</b>
048     *      - does String contains only/none/any of these characters</li>
049     *  <li><b>Substring/Left/Right/Mid</b>
050     *      - null-safe substring extractions</li>
051     *  <li><b>SubstringBefore/SubstringAfter/SubstringBetween</b>
052     *      - substring extraction relative to other strings</li>
053     *  <li><b>Split/Join</b>
054     *      - splits a String into an array of substrings and vice versa</li>
055     *  <li><b>Remove/Delete</b>
056     *      - removes part of a String</li>
057     *  <li><b>Replace/Overlay</b>
058     *      - Searches a String and replaces one String with another</li>
059     *  <li><b>Chomp/Chop</b>
060     *      - removes the last part of a String</li>
061     *  <li><b>LeftPad/RightPad/Center/Repeat</b>
062     *      - pads a String</li>
063     *  <li><b>UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize</b>
064     *      - changes the case of a String</li>
065     *  <li><b>CountMatches</b>
066     *      - counts the number of occurrences of one String in another</li>
067     *  <li><b>IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable</b>
068     *      - checks the characters in a String</li>
069     *  <li><b>DefaultString</b>
070     *      - protects against a null input String</li>
071     *  <li><b>Reverse/ReverseDelimited</b>
072     *      - reverses a String</li>
073     *  <li><b>Abbreviate</b>
074     *      - abbreviates a string using ellipsis</li>
075     *  <li><b>Difference</b>
076     *      - compares Strings and reports on their differences</li>
077     *  <li><b>LevenshteinDistance</b>
078     *      - the number of changes needed to change one String into another</li>
079     * </ul>
080     *
081     * <p>The {@code StringUtils} class defines certain words related to
082     * String handling.</p>
083     *
084     * <ul>
085     *  <li>null - {@code null}</li>
086     *  <li>empty - a zero-length string ({@code ""})</li>
087     *  <li>space - the space character ({@code ' '}, char 32)</li>
088     *  <li>whitespace - the characters defined by {@link Character#isWhitespace(char)}</li>
089     *  <li>trim - the characters &lt;= 32 as in {@link String#trim()}</li>
090     * </ul>
091     *
092     * <p>{@code StringUtils} handles {@code null} input Strings quietly.
093     * That is to say that a {@code null} input will return {@code null}.
094     * Where a {@code boolean} or {@code int} is being returned
095     * details vary by method.</p>
096     *
097     * <p>A side effect of the {@code null} handling is that a
098     * {@code NullPointerException} should be considered a bug in
099     * {@code StringUtils}.</p>
100     *
101     * <p>Methods in this class give sample code to explain their operation.
102     * The symbol {@code *} is used to indicate any input including {@code null}.</p>
103     *
104     * <p>#ThreadSafe#</p>
105     * @see java.lang.String
106     * @since 1.0
107     * @version $Id: StringUtils.java 1144929 2011-07-10 18:26:16Z ggregory $
108     */
109    //@Immutable
110    public class StringUtils {
111        // Performance testing notes (JDK 1.4, Jul03, scolebourne)
112        // Whitespace:
113        // Character.isWhitespace() is faster than WHITESPACE.indexOf()
114        // where WHITESPACE is a string of all whitespace characters
115        //
116        // Character access:
117        // String.charAt(n) versus toCharArray(), then array[n]
118        // String.charAt(n) is about 15% worse for a 10K string
119        // They are about equal for a length 50 string
120        // String.charAt(n) is about 4 times better for a length 3 string
121        // String.charAt(n) is best bet overall
122        //
123        // Append:
124        // String.concat about twice as fast as StringBuffer.append
125        // (not sure who tested this)
126    
127        /**
128         * The empty String {@code ""}.
129         * @since 2.0
130         */
131        public static final String EMPTY = "";
132    
133        /**
134         * Represents a failed index search.
135         * @since 2.1
136         */
137        public static final int INDEX_NOT_FOUND = -1;
138    
139        /**
140         * <p>The maximum size to which the padding constant(s) can expand.</p>
141         */
142        private static final int PAD_LIMIT = 8192;
143    
144        /**
145         * A regex pattern for recognizing blocks of whitespace characters.
146         */
147        private static final Pattern WHITESPACE_BLOCK = Pattern.compile("\\s+");
148    
149        /**
150         * <p>{@code StringUtils} instances should NOT be constructed in
151         * standard programming. Instead, the class should be used as
152         * {@code StringUtils.trim(" foo ");}.</p>
153         *
154         * <p>This constructor is public to permit tools that require a JavaBean
155         * instance to operate.</p>
156         */
157        public StringUtils() {
158            super();
159        }
160    
161        // Empty checks
162        //-----------------------------------------------------------------------
163        /**
164         * <p>Checks if a CharSequence is empty ("") or null.</p>
165         *
166         * <pre>
167         * StringUtils.isEmpty(null)      = true
168         * StringUtils.isEmpty("")        = true
169         * StringUtils.isEmpty(" ")       = false
170         * StringUtils.isEmpty("bob")     = false
171         * StringUtils.isEmpty("  bob  ") = false
172         * </pre>
173         *
174         * <p>NOTE: This method changed in Lang version 2.0.
175         * It no longer trims the CharSequence.
176         * That functionality is available in isBlank().</p>
177         *
178         * @param cs  the CharSequence to check, may be null
179         * @return {@code true} if the CharSequence is empty or null
180         * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
181         */
182        public static boolean isEmpty(CharSequence cs) {
183            return cs == null || cs.length() == 0;
184        }
185    
186        /**
187         * <p>Checks if a CharSequence is not empty ("") and not null.</p>
188         *
189         * <pre>
190         * StringUtils.isNotEmpty(null)      = false
191         * StringUtils.isNotEmpty("")        = false
192         * StringUtils.isNotEmpty(" ")       = true
193         * StringUtils.isNotEmpty("bob")     = true
194         * StringUtils.isNotEmpty("  bob  ") = true
195         * </pre>
196         *
197         * @param cs  the CharSequence to check, may be null
198         * @return {@code true} if the CharSequence is not empty and not null
199         * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence)
200         */
201        public static boolean isNotEmpty(CharSequence cs) {
202            return !StringUtils.isEmpty(cs);
203        }
204    
205        /**
206         * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p>
207         *
208         * <pre>
209         * StringUtils.isBlank(null)      = true
210         * StringUtils.isBlank("")        = true
211         * StringUtils.isBlank(" ")       = true
212         * StringUtils.isBlank("bob")     = false
213         * StringUtils.isBlank("  bob  ") = false
214         * </pre>
215         *
216         * @param cs  the CharSequence to check, may be null
217         * @return {@code true} if the CharSequence is null, empty or whitespace
218         * @since 2.0
219         * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence)
220         */
221        public static boolean isBlank(CharSequence cs) {
222            int strLen;
223            if (cs == null || (strLen = cs.length()) == 0) {
224                return true;
225            }
226            for (int i = 0; i < strLen; i++) {
227                if ((Character.isWhitespace(cs.charAt(i)) == false)) {
228                    return false;
229                }
230            }
231            return true;
232        }
233    
234        /**
235         * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p>
236         *
237         * <pre>
238         * StringUtils.isNotBlank(null)      = false
239         * StringUtils.isNotBlank("")        = false
240         * StringUtils.isNotBlank(" ")       = false
241         * StringUtils.isNotBlank("bob")     = true
242         * StringUtils.isNotBlank("  bob  ") = true
243         * </pre>
244         *
245         * @param cs  the CharSequence to check, may be null
246         * @return {@code true} if the CharSequence is
247         *  not empty and not null and not whitespace
248         * @since 2.0
249         * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence)
250         */
251        public static boolean isNotBlank(CharSequence cs) {
252            return !StringUtils.isBlank(cs);
253        }
254    
255        // Trim
256        //-----------------------------------------------------------------------
257        /**
258         * <p>Removes control characters (char &lt;= 32) from both
259         * ends of this String, handling {@code null} by returning
260         * {@code null}.</p>
261         *
262         * <p>The String is trimmed using {@link String#trim()}.
263         * Trim removes start and end characters &lt;= 32.
264         * To strip whitespace use {@link #strip(String)}.</p>
265         *
266         * <p>To trim your choice of characters, use the
267         * {@link #strip(String, String)} methods.</p>
268         *
269         * <pre>
270         * StringUtils.trim(null)          = null
271         * StringUtils.trim("")            = ""
272         * StringUtils.trim("     ")       = ""
273         * StringUtils.trim("abc")         = "abc"
274         * StringUtils.trim("    abc    ") = "abc"
275         * </pre>
276         *
277         * @param str  the String to be trimmed, may be null
278         * @return the trimmed string, {@code null} if null String input
279         */
280        public static String trim(String str) {
281            return str == null ? null : str.trim();
282        }
283    
284        /**
285         * <p>Removes control characters (char &lt;= 32) from both
286         * ends of this String returning {@code null} if the String is
287         * empty ("") after the trim or if it is {@code null}.
288         *
289         * <p>The String is trimmed using {@link String#trim()}.
290         * Trim removes start and end characters &lt;= 32.
291         * To strip whitespace use {@link #stripToNull(String)}.</p>
292         *
293         * <pre>
294         * StringUtils.trimToNull(null)          = null
295         * StringUtils.trimToNull("")            = null
296         * StringUtils.trimToNull("     ")       = null
297         * StringUtils.trimToNull("abc")         = "abc"
298         * StringUtils.trimToNull("    abc    ") = "abc"
299         * </pre>
300         *
301         * @param str  the String to be trimmed, may be null
302         * @return the trimmed String,
303         *  {@code null} if only chars &lt;= 32, empty or null String input
304         * @since 2.0
305         */
306        public static String trimToNull(String str) {
307            String ts = trim(str);
308            return isEmpty(ts) ? null : ts;
309        }
310    
311        /**
312         * <p>Removes control characters (char &lt;= 32) from both
313         * ends of this String returning an empty String ("") if the String
314         * is empty ("") after the trim or if it is {@code null}.
315         *
316         * <p>The String is trimmed using {@link String#trim()}.
317         * Trim removes start and end characters &lt;= 32.
318         * To strip whitespace use {@link #stripToEmpty(String)}.</p>
319         *
320         * <pre>
321         * StringUtils.trimToEmpty(null)          = ""
322         * StringUtils.trimToEmpty("")            = ""
323         * StringUtils.trimToEmpty("     ")       = ""
324         * StringUtils.trimToEmpty("abc")         = "abc"
325         * StringUtils.trimToEmpty("    abc    ") = "abc"
326         * </pre>
327         *
328         * @param str  the String to be trimmed, may be null
329         * @return the trimmed String, or an empty String if {@code null} input
330         * @since 2.0
331         */
332        public static String trimToEmpty(String str) {
333            return str == null ? EMPTY : str.trim();
334        }
335    
336        // Stripping
337        //-----------------------------------------------------------------------
338        /**
339         * <p>Strips whitespace from the start and end of a String.</p>
340         *
341         * <p>This is similar to {@link #trim(String)} but removes whitespace.
342         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
343         *
344         * <p>A {@code null} input String returns {@code null}.</p>
345         *
346         * <pre>
347         * StringUtils.strip(null)     = null
348         * StringUtils.strip("")       = ""
349         * StringUtils.strip("   ")    = ""
350         * StringUtils.strip("abc")    = "abc"
351         * StringUtils.strip("  abc")  = "abc"
352         * StringUtils.strip("abc  ")  = "abc"
353         * StringUtils.strip(" abc ")  = "abc"
354         * StringUtils.strip(" ab c ") = "ab c"
355         * </pre>
356         *
357         * @param str  the String to remove whitespace from, may be null
358         * @return the stripped String, {@code null} if null String input
359         */
360        public static String strip(String str) {
361            return strip(str, null);
362        }
363    
364        /**
365         * <p>Strips whitespace from the start and end of a String  returning
366         * {@code null} if the String is empty ("") after the strip.</p>
367         *
368         * <p>This is similar to {@link #trimToNull(String)} but removes whitespace.
369         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
370         *
371         * <pre>
372         * StringUtils.stripToNull(null)     = null
373         * StringUtils.stripToNull("")       = null
374         * StringUtils.stripToNull("   ")    = null
375         * StringUtils.stripToNull("abc")    = "abc"
376         * StringUtils.stripToNull("  abc")  = "abc"
377         * StringUtils.stripToNull("abc  ")  = "abc"
378         * StringUtils.stripToNull(" abc ")  = "abc"
379         * StringUtils.stripToNull(" ab c ") = "ab c"
380         * </pre>
381         *
382         * @param str  the String to be stripped, may be null
383         * @return the stripped String,
384         *  {@code null} if whitespace, empty or null String input
385         * @since 2.0
386         */
387        public static String stripToNull(String str) {
388            if (str == null) {
389                return null;
390            }
391            str = strip(str, null);
392            return str.length() == 0 ? null : str;
393        }
394    
395        /**
396         * <p>Strips whitespace from the start and end of a String  returning
397         * an empty String if {@code null} input.</p>
398         *
399         * <p>This is similar to {@link #trimToEmpty(String)} but removes whitespace.
400         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
401         *
402         * <pre>
403         * StringUtils.stripToEmpty(null)     = ""
404         * StringUtils.stripToEmpty("")       = ""
405         * StringUtils.stripToEmpty("   ")    = ""
406         * StringUtils.stripToEmpty("abc")    = "abc"
407         * StringUtils.stripToEmpty("  abc")  = "abc"
408         * StringUtils.stripToEmpty("abc  ")  = "abc"
409         * StringUtils.stripToEmpty(" abc ")  = "abc"
410         * StringUtils.stripToEmpty(" ab c ") = "ab c"
411         * </pre>
412         *
413         * @param str  the String to be stripped, may be null
414         * @return the trimmed String, or an empty String if {@code null} input
415         * @since 2.0
416         */
417        public static String stripToEmpty(String str) {
418            return str == null ? EMPTY : strip(str, null);
419        }
420    
421        /**
422         * <p>Strips any of a set of characters from the start and end of a String.
423         * This is similar to {@link String#trim()} but allows the characters
424         * to be stripped to be controlled.</p>
425         *
426         * <p>A {@code null} input String returns {@code null}.
427         * An empty string ("") input returns the empty string.</p>
428         *
429         * <p>If the stripChars String is {@code null}, whitespace is
430         * stripped as defined by {@link Character#isWhitespace(char)}.
431         * Alternatively use {@link #strip(String)}.</p>
432         *
433         * <pre>
434         * StringUtils.strip(null, *)          = null
435         * StringUtils.strip("", *)            = ""
436         * StringUtils.strip("abc", null)      = "abc"
437         * StringUtils.strip("  abc", null)    = "abc"
438         * StringUtils.strip("abc  ", null)    = "abc"
439         * StringUtils.strip(" abc ", null)    = "abc"
440         * StringUtils.strip("  abcyx", "xyz") = "  abc"
441         * </pre>
442         *
443         * @param str  the String to remove characters from, may be null
444         * @param stripChars  the characters to remove, null treated as whitespace
445         * @return the stripped String, {@code null} if null String input
446         */
447        public static String strip(String str, String stripChars) {
448            if (isEmpty(str)) {
449                return str;
450            }
451            str = stripStart(str, stripChars);
452            return stripEnd(str, stripChars);
453        }
454    
455        /**
456         * <p>Strips any of a set of characters from the start of a String.</p>
457         *
458         * <p>A {@code null} input String returns {@code null}.
459         * An empty string ("") input returns the empty string.</p>
460         *
461         * <p>If the stripChars String is {@code null}, whitespace is
462         * stripped as defined by {@link Character#isWhitespace(char)}.</p>
463         *
464         * <pre>
465         * StringUtils.stripStart(null, *)          = null
466         * StringUtils.stripStart("", *)            = ""
467         * StringUtils.stripStart("abc", "")        = "abc"
468         * StringUtils.stripStart("abc", null)      = "abc"
469         * StringUtils.stripStart("  abc", null)    = "abc"
470         * StringUtils.stripStart("abc  ", null)    = "abc  "
471         * StringUtils.stripStart(" abc ", null)    = "abc "
472         * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
473         * </pre>
474         *
475         * @param str  the String to remove characters from, may be null
476         * @param stripChars  the characters to remove, null treated as whitespace
477         * @return the stripped String, {@code null} if null String input
478         */
479        public static String stripStart(String str, String stripChars) {
480            int strLen;
481            if (str == null || (strLen = str.length()) == 0) {
482                return str;
483            }
484            int start = 0;
485            if (stripChars == null) {
486                while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {
487                    start++;
488                }
489            } else if (stripChars.length() == 0) {
490                return str;
491            } else {
492                while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) {
493                    start++;
494                }
495            }
496            return str.substring(start);
497        }
498    
499        /**
500         * <p>Strips any of a set of characters from the end of a String.</p>
501         *
502         * <p>A {@code null} input String returns {@code null}.
503         * An empty string ("") input returns the empty string.</p>
504         *
505         * <p>If the stripChars String is {@code null}, whitespace is
506         * stripped as defined by {@link Character#isWhitespace(char)}.</p>
507         *
508         * <pre>
509         * StringUtils.stripEnd(null, *)          = null
510         * StringUtils.stripEnd("", *)            = ""
511         * StringUtils.stripEnd("abc", "")        = "abc"
512         * StringUtils.stripEnd("abc", null)      = "abc"
513         * StringUtils.stripEnd("  abc", null)    = "  abc"
514         * StringUtils.stripEnd("abc  ", null)    = "abc"
515         * StringUtils.stripEnd(" abc ", null)    = " abc"
516         * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
517         * StringUtils.stripEnd("120.00", ".0")   = "12"
518         * </pre>
519         *
520         * @param str  the String to remove characters from, may be null
521         * @param stripChars  the set of characters to remove, null treated as whitespace
522         * @return the stripped String, {@code null} if null String input
523         */
524        public static String stripEnd(String str, String stripChars) {
525            int end;
526            if (str == null || (end = str.length()) == 0) {
527                return str;
528            }
529    
530            if (stripChars == null) {
531                while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
532                    end--;
533                }
534            } else if (stripChars.length() == 0) {
535                return str;
536            } else {
537                while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) {
538                    end--;
539                }
540            }
541            return str.substring(0, end);
542        }
543    
544        // StripAll
545        //-----------------------------------------------------------------------
546        /**
547         * <p>Strips whitespace from the start and end of every String in an array.
548         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
549         *
550         * <p>A new array is returned each time, except for length zero.
551         * A {@code null} array will return {@code null}.
552         * An empty array will return itself.
553         * A {@code null} array entry will be ignored.</p>
554         *
555         * <pre>
556         * StringUtils.stripAll(null)             = null
557         * StringUtils.stripAll([])               = []
558         * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
559         * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
560         * </pre>
561         *
562         * @param strs  the array to remove whitespace from, may be null
563         * @return the stripped Strings, {@code null} if null array input
564         */
565        public static String[] stripAll(String... strs) {
566            return stripAll(strs, null);
567        }
568    
569        /**
570         * <p>Strips any of a set of characters from the start and end of every
571         * String in an array.</p>
572         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
573         *
574         * <p>A new array is returned each time, except for length zero.
575         * A {@code null} array will return {@code null}.
576         * An empty array will return itself.
577         * A {@code null} array entry will be ignored.
578         * A {@code null} stripChars will strip whitespace as defined by
579         * {@link Character#isWhitespace(char)}.</p>
580         *
581         * <pre>
582         * StringUtils.stripAll(null, *)                = null
583         * StringUtils.stripAll([], *)                  = []
584         * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
585         * StringUtils.stripAll(["abc  ", null], null)  = ["abc", null]
586         * StringUtils.stripAll(["abc  ", null], "yz")  = ["abc  ", null]
587         * StringUtils.stripAll(["yabcz", null], "yz")  = ["abc", null]
588         * </pre>
589         *
590         * @param strs  the array to remove characters from, may be null
591         * @param stripChars  the characters to remove, null treated as whitespace
592         * @return the stripped Strings, {@code null} if null array input
593         */
594        public static String[] stripAll(String[] strs, String stripChars) {
595            int strsLen;
596            if (strs == null || (strsLen = strs.length) == 0) {
597                return strs;
598            }
599            String[] newArr = new String[strsLen];
600            for (int i = 0; i < strsLen; i++) {
601                newArr[i] = strip(strs[i], stripChars);
602            }
603            return newArr;
604        }
605    
606        /**
607         * <p>Removes diacritics (~= accents) from a string. The case will not be altered.</p>
608         * <p>For instance, '&agrave;' will be replaced by 'a'.</p>
609         * <p>Note that ligatures will be left as is.</p>
610         *
611         * <p>This method will use the first available implementation of:
612         * Java 6's {@link java.text.Normalizer}, Java 1.3&ndash;1.5's {@code sun.text.Normalizer}</p>
613         *
614         * <pre>
615         * StringUtils.stripAccents(null)                = null
616         * StringUtils.stripAccents("")                  = ""
617         * StringUtils.stripAccents("control")           = "control"
618         * StringUtils.stripAccents("&eacute;clair")     = "eclair"
619         * </pre>
620         *
621         * @param input String to be stripped
622         * @return input text with diacritics removed
623         *
624         * @since 3.0
625         */
626        // See also Lucene's ASCIIFoldingFilter (Lucene 2.9) that replaces accented characters by their unaccented equivalent (and uncommitted bug fix: https://issues.apache.org/jira/browse/LUCENE-1343?focusedCommentId=12858907&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12858907).
627        public static String stripAccents(String input) {
628            if(input == null) {
629                return null;
630            }
631            try {
632                String result = null;
633                if (java6Available) {
634                    result = removeAccentsJava6(input);
635                } else if (sunAvailable) {
636                    result = removeAccentsSUN(input);
637                } else {
638                    throw new UnsupportedOperationException(
639                        "The stripAccents(CharSequence) method requires at least Java 1.6 or a Sun JVM");
640                }
641                // Note that none of the above methods correctly remove ligatures...
642                return result;
643            } catch(IllegalArgumentException iae) {
644                throw new RuntimeException("IllegalArgumentException occurred", iae);
645            } catch(IllegalAccessException iae) {
646                throw new RuntimeException("IllegalAccessException occurred", iae);
647            } catch(InvocationTargetException ite) {
648                throw new RuntimeException("InvocationTargetException occurred", ite);
649            } catch(SecurityException se) {
650                throw new RuntimeException("SecurityException occurred", se);
651            }
652        }
653    
654        /**
655         * Use {@code java.text.Normalizer#normalize(CharSequence, Normalizer.Form)}
656         * (but be careful, this class exists in Java 1.3, with an entirely different meaning!)
657         *
658         * @param text the text to be processed
659         * @return the processed string
660         * @throws IllegalAccessException may be thrown by a reflection call
661         * @throws InvocationTargetException if a reflection call throws an exception
662         * @throws IllegalStateException if the {@code Normalizer} class is not available
663         */
664        private static String removeAccentsJava6(CharSequence text)
665            throws IllegalAccessException, InvocationTargetException {
666            /*
667            String decomposed = java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD);
668            return java6Pattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$
669            */
670            if (!java6Available || java6NormalizerFormNFD == null) {
671                throw new IllegalStateException("java.text.Normalizer is not available");
672            }
673            String result;
674            result = (String) java6NormalizeMethod.invoke(null, new Object[] {text, java6NormalizerFormNFD});
675            result = java6Pattern.matcher(result).replaceAll("");//$NON-NLS-1$
676            return result;
677        }
678    
679        /**
680         * Use {@code sun.text.Normalizer#decompose(String, boolean, int)}
681         *
682         * @param text the text to be processed
683         * @return the processed string
684         * @throws IllegalAccessException may be thrown by a reflection call
685         * @throws InvocationTargetException if a reflection call throws an exception
686         * @throws IllegalStateException if the {@code Normalizer} class is not available
687         */
688        private static String removeAccentsSUN(CharSequence text)
689            throws IllegalAccessException, InvocationTargetException {
690            /*
691            String decomposed = sun.text.Normalizer.decompose(text, false, 0);
692            return sunPattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$
693            */
694            if (! sunAvailable) {
695                throw new IllegalStateException("sun.text.Normalizer is not available");
696            }
697            String result;
698            result = (String) sunDecomposeMethod.invoke(null, new Object[] {text, Boolean.FALSE, Integer.valueOf(0)});
699            result = sunPattern.matcher(result).replaceAll("");//$NON-NLS-1$
700            return result;
701        }
702    
703        // SUN internal, Java 1.3 -> Java 5
704        private static boolean sunAvailable = false;
705        private static Method  sunDecomposeMethod = null;
706        private static final Pattern sunPattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");//$NON-NLS-1$
707        // Java 6+
708        private static boolean java6Available = false;
709        private static Method  java6NormalizeMethod = null;
710        private static Object  java6NormalizerFormNFD = null;
711        private static final Pattern java6Pattern = sunPattern;
712    
713        static {
714            try {
715                // java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD);
716                // Be careful not to get Java 1.3 java.text.Normalizer!
717                Class<?> normalizerFormClass = Thread.currentThread().getContextClassLoader()
718                    .loadClass("java.text.Normalizer$Form");//$NON-NLS-1$
719                java6NormalizerFormNFD = normalizerFormClass.getField("NFD").get(null);//$NON-NLS-1$
720                Class<?> normalizerClass = Thread.currentThread().getContextClassLoader()
721                    .loadClass("java.text.Normalizer");//$NON-NLS-1$
722                java6NormalizeMethod = normalizerClass.getMethod("normalize",
723                        new Class[] {CharSequence.class, normalizerFormClass});//$NON-NLS-1$
724                java6Available = true;
725            } catch (ClassNotFoundException e) {
726                java6Available = false;
727            } catch (NoSuchFieldException e) {
728                java6Available = false;
729            } catch (IllegalAccessException e) {
730                java6Available = false;
731            } catch (NoSuchMethodException e) {
732                java6Available = false;
733            }
734    
735            try {
736                // sun.text.Normalizer.decompose(text, false, 0);
737                Class<?> normalizerClass = Thread.currentThread().getContextClassLoader()
738                    .loadClass("sun.text.Normalizer");//$NON-NLS-1$
739                sunDecomposeMethod = normalizerClass.getMethod("decompose",
740                        new Class[] {String.class, Boolean.TYPE, Integer.TYPE});//$NON-NLS-1$
741                sunAvailable = true;
742            } catch (ClassNotFoundException e) {
743                sunAvailable = false;
744            } catch (NoSuchMethodException e) {
745                sunAvailable = false;
746            }
747        }
748    
749        // Equals
750        //-----------------------------------------------------------------------
751        /**
752         * <p>Compares two CharSequences, returning {@code true} if they are equal.</p>
753         *
754         * <p>{@code null}s are handled without exceptions. Two {@code null}
755         * references are considered to be equal. The comparison is case sensitive.</p>
756         *
757         * <pre>
758         * StringUtils.equals(null, null)   = true
759         * StringUtils.equals(null, "abc")  = false
760         * StringUtils.equals("abc", null)  = false
761         * StringUtils.equals("abc", "abc") = true
762         * StringUtils.equals("abc", "ABC") = false
763         * </pre>
764         *
765         * @see java.lang.String#equals(Object)
766         * @param cs1  the first CharSequence, may be null
767         * @param cs2  the second CharSequence, may be null
768         * @return {@code true} if the CharSequences are equal, case sensitive, or
769         *  both {@code null}
770         * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence)
771         */
772        public static boolean equals(CharSequence cs1, CharSequence cs2) {
773            return cs1 == null ? cs2 == null : cs1.equals(cs2);
774        }
775    
776        /**
777         * <p>Compares two CharSequences, returning {@code true} if they are equal ignoring
778         * the case.</p>
779         *
780         * <p>{@code null}s are handled without exceptions. Two {@code null}
781         * references are considered equal. Comparison is case insensitive.</p>
782         *
783         * <pre>
784         * StringUtils.equalsIgnoreCase(null, null)   = true
785         * StringUtils.equalsIgnoreCase(null, "abc")  = false
786         * StringUtils.equalsIgnoreCase("abc", null)  = false
787         * StringUtils.equalsIgnoreCase("abc", "abc") = true
788         * StringUtils.equalsIgnoreCase("abc", "ABC") = true
789         * </pre>
790         *
791         * @param str1  the first CharSequence, may be null
792         * @param str2  the second CharSequence, may be null
793         * @return {@code true} if the CharSequence are equal, case insensitive, or
794         *  both {@code null}
795         * @since 3.0 Changed signature from equalsIgnoreCase(String, String) to equalsIgnoreCase(CharSequence, CharSequence)
796         */
797        public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
798            if (str1 == null || str2 == null) {
799                return str1 == str2;
800            } else {
801                return CharSequenceUtils.regionMatches(str1, true, 0, str2, 0, Math.max(str1.length(), str2.length()));
802            }
803        }
804    
805        // IndexOf
806        //-----------------------------------------------------------------------
807        /**
808         * <p>Finds the first index within a CharSequence, handling {@code null}.
809         * This method uses {@link String#indexOf(int, int)} if possible.</p>
810         *
811         * <p>A {@code null} or empty ("") CharSequence will return {@code INDEX_NOT_FOUND (-1)}.</p>
812         *
813         * <pre>
814         * StringUtils.indexOf(null, *)         = -1
815         * StringUtils.indexOf("", *)           = -1
816         * StringUtils.indexOf("aabaabaa", 'a') = 0
817         * StringUtils.indexOf("aabaabaa", 'b') = 2
818         * </pre>
819         *
820         * @param seq  the CharSequence to check, may be null
821         * @param searchChar  the character to find
822         * @return the first index of the search character,
823         *  -1 if no match or {@code null} string input
824         * @since 2.0
825         * @since 3.0 Changed signature from indexOf(String, int) to indexOf(CharSequence, int)
826         */
827        public static int indexOf(CharSequence seq, int searchChar) {
828            if (isEmpty(seq)) {
829                return INDEX_NOT_FOUND;
830            }
831            return CharSequenceUtils.indexOf(seq, searchChar, 0);
832        }
833    
834        /**
835         * <p>Finds the first index within a CharSequence from a start position,
836         * handling {@code null}.
837         * This method uses {@link String#indexOf(int, int)} if possible.</p>
838         *
839         * <p>A {@code null} or empty ("") CharSequence will return {@code (INDEX_NOT_FOUND) -1}.
840         * A negative start position is treated as zero.
841         * A start position greater than the string length returns {@code -1}.</p>
842         *
843         * <pre>
844         * StringUtils.indexOf(null, *, *)          = -1
845         * StringUtils.indexOf("", *, *)            = -1
846         * StringUtils.indexOf("aabaabaa", 'b', 0)  = 2
847         * StringUtils.indexOf("aabaabaa", 'b', 3)  = 5
848         * StringUtils.indexOf("aabaabaa", 'b', 9)  = -1
849         * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
850         * </pre>
851         *
852         * @param seq  the CharSequence to check, may be null
853         * @param searchChar  the character to find
854         * @param startPos  the start position, negative treated as zero
855         * @return the first index of the search character,
856         *  -1 if no match or {@code null} string input
857         * @since 2.0
858         * @since 3.0 Changed signature from indexOf(String, int, int) to indexOf(CharSequence, int, int)
859         */
860        public static int indexOf(CharSequence seq, int searchChar, int startPos) {
861            if (isEmpty(seq)) {
862                return INDEX_NOT_FOUND;
863            }
864            return CharSequenceUtils.indexOf(seq, searchChar, startPos);
865        }
866    
867        /**
868         * <p>Finds the first index within a CharSequence, handling {@code null}.
869         * This method uses {@link String#indexOf(String, int)} if possible.</p>
870         *
871         * <p>A {@code null} CharSequence will return {@code -1}.</p>
872         *
873         * <pre>
874         * StringUtils.indexOf(null, *)          = -1
875         * StringUtils.indexOf(*, null)          = -1
876         * StringUtils.indexOf("", "")           = 0
877         * StringUtils.indexOf("", *)            = -1 (except when * = "")
878         * StringUtils.indexOf("aabaabaa", "a")  = 0
879         * StringUtils.indexOf("aabaabaa", "b")  = 2
880         * StringUtils.indexOf("aabaabaa", "ab") = 1
881         * StringUtils.indexOf("aabaabaa", "")   = 0
882         * </pre>
883         *
884         * @param seq  the CharSequence to check, may be null
885         * @param searchSeq  the CharSequence to find, may be null
886         * @return the first index of the search CharSequence,
887         *  -1 if no match or {@code null} string input
888         * @since 2.0
889         * @since 3.0 Changed signature from indexOf(String, String) to indexOf(CharSequence, CharSequence)
890         */
891        public static int indexOf(CharSequence seq, CharSequence searchSeq) {
892            if (seq == null || searchSeq == null) {
893                return INDEX_NOT_FOUND;
894            }
895            return CharSequenceUtils.indexOf(seq, searchSeq, 0);
896        }
897    
898        /**
899         * <p>Finds the first index within a CharSequence, handling {@code null}.
900         * This method uses {@link String#indexOf(String, int)} if possible.</p>
901         *
902         * <p>A {@code null} CharSequence will return {@code -1}.
903         * A negative start position is treated as zero.
904         * An empty ("") search CharSequence always matches.
905         * A start position greater than the string length only matches
906         * an empty search CharSequence.</p>
907         *
908         * <pre>
909         * StringUtils.indexOf(null, *, *)          = -1
910         * StringUtils.indexOf(*, null, *)          = -1
911         * StringUtils.indexOf("", "", 0)           = 0
912         * StringUtils.indexOf("", *, 0)            = -1 (except when * = "")
913         * StringUtils.indexOf("aabaabaa", "a", 0)  = 0
914         * StringUtils.indexOf("aabaabaa", "b", 0)  = 2
915         * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
916         * StringUtils.indexOf("aabaabaa", "b", 3)  = 5
917         * StringUtils.indexOf("aabaabaa", "b", 9)  = -1
918         * StringUtils.indexOf("aabaabaa", "b", -1) = 2
919         * StringUtils.indexOf("aabaabaa", "", 2)   = 2
920         * StringUtils.indexOf("abc", "", 9)        = 3
921         * </pre>
922         *
923         * @param seq  the CharSequence to check, may be null
924         * @param searchSeq  the CharSequence to find, may be null
925         * @param startPos  the start position, negative treated as zero
926         * @return the first index of the search CharSequence,
927         *  -1 if no match or {@code null} string input
928         * @since 2.0
929         * @since 3.0 Changed signature from indexOf(String, String, int) to indexOf(CharSequence, CharSequence, int)
930         */
931        public static int indexOf(CharSequence seq, CharSequence searchSeq, int startPos) {
932            if (seq == null || searchSeq == null) {
933                return INDEX_NOT_FOUND;
934            }
935            return CharSequenceUtils.indexOf(seq, searchSeq, startPos);
936        }
937    
938        /**
939         * <p>Finds the n-th index within a CharSequence, handling {@code null}.
940         * This method uses {@link String#indexOf(String)} if possible.</p>
941         *
942         * <p>A {@code null} CharSequence will return {@code -1}.</p>
943         *
944         * <pre>
945         * StringUtils.ordinalIndexOf(null, *, *)          = -1
946         * StringUtils.ordinalIndexOf(*, null, *)          = -1
947         * StringUtils.ordinalIndexOf("", "", *)           = 0
948         * StringUtils.ordinalIndexOf("aabaabaa", "a", 1)  = 0
949         * StringUtils.ordinalIndexOf("aabaabaa", "a", 2)  = 1
950         * StringUtils.ordinalIndexOf("aabaabaa", "b", 1)  = 2
951         * StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  = 5
952         * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
953         * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
954         * StringUtils.ordinalIndexOf("aabaabaa", "", 1)   = 0
955         * StringUtils.ordinalIndexOf("aabaabaa", "", 2)   = 0
956         * </pre>
957         *
958         * <p>Note that 'head(CharSequence str, int n)' may be implemented as: </p>
959         *
960         * <pre>
961         *   str.substring(0, lastOrdinalIndexOf(str, "\n", n))
962         * </pre>
963         *
964         * @param str  the CharSequence to check, may be null
965         * @param searchStr  the CharSequence to find, may be null
966         * @param ordinal  the n-th {@code searchStr} to find
967         * @return the n-th index of the search CharSequence,
968         *  {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
969         * @since 2.1
970         * @since 3.0 Changed signature from ordinalIndexOf(String, String, int) to ordinalIndexOf(CharSequence, CharSequence, int)
971         */
972        public static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) {
973            return ordinalIndexOf(str, searchStr, ordinal, false);
974        }
975    
976        /**
977         * <p>Finds the n-th index within a String, handling {@code null}.
978         * This method uses {@link String#indexOf(String)} if possible.</p>
979         *
980         * <p>A {@code null} CharSequence will return {@code -1}.</p>
981         *
982         * @param str  the CharSequence to check, may be null
983         * @param searchStr  the CharSequence to find, may be null
984         * @param ordinal  the n-th {@code searchStr} to find
985         * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf()
986         * @return the n-th index of the search CharSequence,
987         *  {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
988         */
989        // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int)
990        private static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal, boolean lastIndex) {
991            if (str == null || searchStr == null || ordinal <= 0) {
992                return INDEX_NOT_FOUND;
993            }
994            if (searchStr.length() == 0) {
995                return lastIndex ? str.length() : 0;
996            }
997            int found = 0;
998            int index = lastIndex ? str.length() : INDEX_NOT_FOUND;
999            do {
1000                if (lastIndex) {
1001                    index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1);
1002                } else {
1003                    index = CharSequenceUtils.indexOf(str, searchStr, index + 1);
1004                }
1005                if (index < 0) {
1006                    return index;
1007                }
1008                found++;
1009            } while (found < ordinal);
1010            return index;
1011        }
1012    
1013        /**
1014         * <p>Case in-sensitive find of the first index within a CharSequence.</p>
1015         *
1016         * <p>A {@code null} CharSequence will return {@code -1}.
1017         * A negative start position is treated as zero.
1018         * An empty ("") search CharSequence always matches.
1019         * A start position greater than the string length only matches
1020         * an empty search CharSequence.</p>
1021         *
1022         * <pre>
1023         * StringUtils.indexOfIgnoreCase(null, *)          = -1
1024         * StringUtils.indexOfIgnoreCase(*, null)          = -1
1025         * StringUtils.indexOfIgnoreCase("", "")           = 0
1026         * StringUtils.indexOfIgnoreCase("aabaabaa", "a")  = 0
1027         * StringUtils.indexOfIgnoreCase("aabaabaa", "b")  = 2
1028         * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
1029         * </pre>
1030         *
1031         * @param str  the CharSequence to check, may be null
1032         * @param searchStr  the CharSequence to find, may be null
1033         * @return the first index of the search CharSequence,
1034         *  -1 if no match or {@code null} string input
1035         * @since 2.5
1036         * @since 3.0 Changed signature from indexOfIgnoreCase(String, String) to indexOfIgnoreCase(CharSequence, CharSequence)
1037         */
1038        public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
1039            return indexOfIgnoreCase(str, searchStr, 0);
1040        }
1041    
1042        /**
1043         * <p>Case in-sensitive find of the first index within a CharSequence
1044         * from the specified position.</p>
1045         *
1046         * <p>A {@code null} CharSequence will return {@code -1}.
1047         * A negative start position is treated as zero.
1048         * An empty ("") search CharSequence always matches.
1049         * A start position greater than the string length only matches
1050         * an empty search CharSequence.</p>
1051         *
1052         * <pre>
1053         * StringUtils.indexOfIgnoreCase(null, *, *)          = -1
1054         * StringUtils.indexOfIgnoreCase(*, null, *)          = -1
1055         * StringUtils.indexOfIgnoreCase("", "", 0)           = 0
1056         * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
1057         * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
1058         * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
1059         * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
1060         * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
1061         * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
1062         * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
1063         * StringUtils.indexOfIgnoreCase("abc", "", 9)        = 3
1064         * </pre>
1065         *
1066         * @param str  the CharSequence to check, may be null
1067         * @param searchStr  the CharSequence to find, may be null
1068         * @param startPos  the start position, negative treated as zero
1069         * @return the first index of the search CharSequence,
1070         *  -1 if no match or {@code null} string input
1071         * @since 2.5
1072         * @since 3.0 Changed signature from indexOfIgnoreCase(String, String, int) to indexOfIgnoreCase(CharSequence, CharSequence, int)
1073         */
1074        public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) {
1075            if (str == null || searchStr == null) {
1076                return INDEX_NOT_FOUND;
1077            }
1078            if (startPos < 0) {
1079                startPos = 0;
1080            }
1081            int endLimit = (str.length() - searchStr.length()) + 1;
1082            if (startPos > endLimit) {
1083                return INDEX_NOT_FOUND;
1084            }
1085            if (searchStr.length() == 0) {
1086                return startPos;
1087            }
1088            for (int i = startPos; i < endLimit; i++) {
1089                if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) {
1090                    return i;
1091                }
1092            }
1093            return INDEX_NOT_FOUND;
1094        }
1095    
1096        // LastIndexOf
1097        //-----------------------------------------------------------------------
1098        /**
1099         * <p>Finds the last index within a CharSequence, handling {@code null}.
1100         * This method uses {@link String#lastIndexOf(int)} if possible.</p>
1101         *
1102         * <p>A {@code null} or empty ("") CharSequence will return {@code -1}.</p>
1103         *
1104         * <pre>
1105         * StringUtils.lastIndexOf(null, *)         = -1
1106         * StringUtils.lastIndexOf("", *)           = -1
1107         * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
1108         * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
1109         * </pre>
1110         *
1111         * @param seq  the CharSequence to check, may be null
1112         * @param searchChar  the character to find
1113         * @return the last index of the search character,
1114         *  -1 if no match or {@code null} string input
1115         * @since 2.0
1116         * @since 3.0 Changed signature from lastIndexOf(String, int) to lastIndexOf(CharSequence, int)
1117         */
1118        public static int lastIndexOf(CharSequence seq, int searchChar) {
1119            if (isEmpty(seq)) {
1120                return INDEX_NOT_FOUND;
1121            }
1122            return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length());
1123        }
1124    
1125        /**
1126         * <p>Finds the last index within a CharSequence from a start position,
1127         * handling {@code null}.
1128         * This method uses {@link String#lastIndexOf(int, int)} if possible.</p>
1129         *
1130         * <p>A {@code null} or empty ("") CharSequence will return {@code -1}.
1131         * A negative start position returns {@code -1}.
1132         * A start position greater than the string length searches the whole string.</p>
1133         *
1134         * <pre>
1135         * StringUtils.lastIndexOf(null, *, *)          = -1
1136         * StringUtils.lastIndexOf("", *,  *)           = -1
1137         * StringUtils.lastIndexOf("aabaabaa", 'b', 8)  = 5
1138         * StringUtils.lastIndexOf("aabaabaa", 'b', 4)  = 2
1139         * StringUtils.lastIndexOf("aabaabaa", 'b', 0)  = -1
1140         * StringUtils.lastIndexOf("aabaabaa", 'b', 9)  = 5
1141         * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
1142         * StringUtils.lastIndexOf("aabaabaa", 'a', 0)  = 0
1143         * </pre>
1144         *
1145         * @param seq  the CharSequence to check, may be null
1146         * @param searchChar  the character to find
1147         * @param startPos  the start position
1148         * @return the last index of the search character,
1149         *  -1 if no match or {@code null} string input
1150         * @since 2.0
1151         * @since 3.0 Changed signature from lastIndexOf(String, int, int) to lastIndexOf(CharSequence, int, int)
1152         */
1153        public static int lastIndexOf(CharSequence seq, int searchChar, int startPos) {
1154            if (isEmpty(seq)) {
1155                return INDEX_NOT_FOUND;
1156            }
1157            return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos);
1158        }
1159    
1160        /**
1161         * <p>Finds the last index within a CharSequence, handling {@code null}.
1162         * This method uses {@link String#lastIndexOf(String)} if possible.</p>
1163         *
1164         * <p>A {@code null} CharSequence will return {@code -1}.</p>
1165         *
1166         * <pre>
1167         * StringUtils.lastIndexOf(null, *)          = -1
1168         * StringUtils.lastIndexOf(*, null)          = -1
1169         * StringUtils.lastIndexOf("", "")           = 0
1170         * StringUtils.lastIndexOf("aabaabaa", "a")  = 7
1171         * StringUtils.lastIndexOf("aabaabaa", "b")  = 5
1172         * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
1173         * StringUtils.lastIndexOf("aabaabaa", "")   = 8
1174         * </pre>
1175         *
1176         * @param seq  the CharSequence to check, may be null
1177         * @param searchSeq  the CharSequence to find, may be null
1178         * @return the last index of the search String,
1179         *  -1 if no match or {@code null} string input
1180         * @since 2.0
1181         * @since 3.0 Changed signature from lastIndexOf(String, String) to lastIndexOf(CharSequence, CharSequence)
1182         */
1183        public static int lastIndexOf(CharSequence seq, CharSequence searchSeq) {
1184            if (seq == null || searchSeq == null) {
1185                return INDEX_NOT_FOUND;
1186            }
1187            return CharSequenceUtils.lastIndexOf(seq, searchSeq, seq.length());
1188        }
1189    
1190        /**
1191         * <p>Finds the n-th last index within a String, handling {@code null}.
1192         * This method uses {@link String#lastIndexOf(String)}.</p>
1193         *
1194         * <p>A {@code null} String will return {@code -1}.</p>
1195         *
1196         * <pre>
1197         * StringUtils.lastOrdinalIndexOf(null, *, *)          = -1
1198         * StringUtils.lastOrdinalIndexOf(*, null, *)          = -1
1199         * StringUtils.lastOrdinalIndexOf("", "", *)           = 0
1200         * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
1201         * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
1202         * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
1203         * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
1204         * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
1205         * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
1206         * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
1207         * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
1208         * </pre>
1209         *
1210         * <p>Note that 'tail(CharSequence str, int n)' may be implemented as: </p>
1211         *
1212         * <pre>
1213         *   str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
1214         * </pre>
1215         *
1216         * @param str  the CharSequence to check, may be null
1217         * @param searchStr  the CharSequence to find, may be null
1218         * @param ordinal  the n-th last {@code searchStr} to find
1219         * @return the n-th last index of the search CharSequence,
1220         *  {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
1221         * @since 2.5
1222         * @since 3.0 Changed signature from lastOrdinalIndexOf(String, String, int) to lastOrdinalIndexOf(CharSequence, CharSequence, int)
1223         */
1224        public static int lastOrdinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) {
1225            return ordinalIndexOf(str, searchStr, ordinal, true);
1226        }
1227    
1228        /**
1229         * <p>Finds the first index within a CharSequence, handling {@code null}.
1230         * This method uses {@link String#lastIndexOf(String, int)} if possible.</p>
1231         *
1232         * <p>A {@code null} CharSequence will return {@code -1}.
1233         * A negative start position returns {@code -1}.
1234         * An empty ("") search CharSequence always matches unless the start position is negative.
1235         * A start position greater than the string length searches the whole string.</p>
1236         *
1237         * <pre>
1238         * StringUtils.lastIndexOf(null, *, *)          = -1
1239         * StringUtils.lastIndexOf(*, null, *)          = -1
1240         * StringUtils.lastIndexOf("aabaabaa", "a", 8)  = 7
1241         * StringUtils.lastIndexOf("aabaabaa", "b", 8)  = 5
1242         * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4
1243         * StringUtils.lastIndexOf("aabaabaa", "b", 9)  = 5
1244         * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1
1245         * StringUtils.lastIndexOf("aabaabaa", "a", 0)  = 0
1246         * StringUtils.lastIndexOf("aabaabaa", "b", 0)  = -1
1247         * </pre>
1248         *
1249         * @param seq  the CharSequence to check, may be null
1250         * @param searchSeq  the CharSequence to find, may be null
1251         * @param startPos  the start position, negative treated as zero
1252         * @return the first index of the search CharSequence,
1253         *  -1 if no match or {@code null} string input
1254         * @since 2.0
1255         * @since 3.0 Changed signature from lastIndexOf(String, String, int) to lastIndexOf(CharSequence, CharSequence, int)
1256         */
1257        public static int lastIndexOf(CharSequence seq, CharSequence searchSeq, int startPos) {
1258            if (seq == null || searchSeq == null) {
1259                return INDEX_NOT_FOUND;
1260            }
1261            return CharSequenceUtils.lastIndexOf(seq, searchSeq, startPos);
1262        }
1263    
1264        /**
1265         * <p>Case in-sensitive find of the last index within a CharSequence.</p>
1266         *
1267         * <p>A {@code null} CharSequence will return {@code -1}.
1268         * A negative start position returns {@code -1}.
1269         * An empty ("") search CharSequence always matches unless the start position is negative.
1270         * A start position greater than the string length searches the whole string.</p>
1271         *
1272         * <pre>
1273         * StringUtils.lastIndexOfIgnoreCase(null, *)          = -1
1274         * StringUtils.lastIndexOfIgnoreCase(*, null)          = -1
1275         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
1276         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
1277         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
1278         * </pre>
1279         *
1280         * @param str  the CharSequence to check, may be null
1281         * @param searchStr  the CharSequence to find, may be null
1282         * @return the first index of the search CharSequence,
1283         *  -1 if no match or {@code null} string input
1284         * @since 2.5
1285         * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String) to lastIndexOfIgnoreCase(CharSequence, CharSequence)
1286         */
1287        public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
1288            if (str == null || searchStr == null) {
1289                return INDEX_NOT_FOUND;
1290            }
1291            return lastIndexOfIgnoreCase(str, searchStr, str.length());
1292        }
1293    
1294        /**
1295         * <p>Case in-sensitive find of the last index within a CharSequence
1296         * from the specified position.</p>
1297         *
1298         * <p>A {@code null} CharSequence will return {@code -1}.
1299         * A negative start position returns {@code -1}.
1300         * An empty ("") search CharSequence always matches unless the start position is negative.
1301         * A start position greater than the string length searches the whole string.</p>
1302         *
1303         * <pre>
1304         * StringUtils.lastIndexOfIgnoreCase(null, *, *)          = -1
1305         * StringUtils.lastIndexOfIgnoreCase(*, null, *)          = -1
1306         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
1307         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
1308         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
1309         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
1310         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
1311         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
1312         * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
1313         * </pre>
1314         *
1315         * @param str  the CharSequence to check, may be null
1316         * @param searchStr  the CharSequence to find, may be null
1317         * @param startPos  the start position
1318         * @return the first index of the search CharSequence,
1319         *  -1 if no match or {@code null} input
1320         * @since 2.5
1321         * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String, int) to lastIndexOfIgnoreCase(CharSequence, CharSequence, int)
1322         */
1323        public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) {
1324            if (str == null || searchStr == null) {
1325                return INDEX_NOT_FOUND;
1326            }
1327            if (startPos > (str.length() - searchStr.length())) {
1328                startPos = str.length() - searchStr.length();
1329            }
1330            if (startPos < 0) {
1331                return INDEX_NOT_FOUND;
1332            }
1333            if (searchStr.length() == 0) {
1334                return startPos;
1335            }
1336    
1337            for (int i = startPos; i >= 0; i--) {
1338                if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) {
1339                    return i;
1340                }
1341            }
1342            return INDEX_NOT_FOUND;
1343        }
1344    
1345        // Contains
1346        //-----------------------------------------------------------------------
1347        /**
1348         * <p>Checks if CharSequence contains a search character, handling {@code null}.
1349         * This method uses {@link String#indexOf(int)} if possible.</p>
1350         *
1351         * <p>A {@code null} or empty ("") CharSequence will return {@code false}.</p>
1352         *
1353         * <pre>
1354         * StringUtils.contains(null, *)    = false
1355         * StringUtils.contains("", *)      = false
1356         * StringUtils.contains("abc", 'a') = true
1357         * StringUtils.contains("abc", 'z') = false
1358         * </pre>
1359         *
1360         * @param seq  the CharSequence to check, may be null
1361         * @param searchChar  the character to find
1362         * @return true if the CharSequence contains the search character,
1363         *  false if not or {@code null} string input
1364         * @since 2.0
1365         * @since 3.0 Changed signature from contains(String, int) to contains(CharSequence, int)
1366         */
1367        public static boolean contains(CharSequence seq, int searchChar) {
1368            if (isEmpty(seq)) {
1369                return false;
1370            }
1371            return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0;
1372        }
1373    
1374        /**
1375         * <p>Checks if CharSequence contains a search CharSequence, handling {@code null}.
1376         * This method uses {@link String#indexOf(String)} if possible.</p>
1377         *
1378         * <p>A {@code null} CharSequence will return {@code false}.</p>
1379         *
1380         * <pre>
1381         * StringUtils.contains(null, *)     = false
1382         * StringUtils.contains(*, null)     = false
1383         * StringUtils.contains("", "")      = true
1384         * StringUtils.contains("abc", "")   = true
1385         * StringUtils.contains("abc", "a")  = true
1386         * StringUtils.contains("abc", "z")  = false
1387         * </pre>
1388         *
1389         * @param seq  the CharSequence to check, may be null
1390         * @param searchSeq  the CharSequence to find, may be null
1391         * @return true if the CharSequence contains the search CharSequence,
1392         *  false if not or {@code null} string input
1393         * @since 2.0
1394         * @since 3.0 Changed signature from contains(String, String) to contains(CharSequence, CharSequence)
1395         */
1396        public static boolean contains(CharSequence seq, CharSequence searchSeq) {
1397            if (seq == null || searchSeq == null) {
1398                return false;
1399            }
1400            return CharSequenceUtils.indexOf(seq, searchSeq, 0) >= 0;
1401        }
1402    
1403        /**
1404         * <p>Checks if CharSequence contains a search CharSequence irrespective of case,
1405         * handling {@code null}. Case-insensitivity is defined as by
1406         * {@link String#equalsIgnoreCase(String)}.
1407         *
1408         * <p>A {@code null} CharSequence will return {@code false}.</p>
1409         *
1410         * <pre>
1411         * StringUtils.contains(null, *) = false
1412         * StringUtils.contains(*, null) = false
1413         * StringUtils.contains("", "") = true
1414         * StringUtils.contains("abc", "") = true
1415         * StringUtils.contains("abc", "a") = true
1416         * StringUtils.contains("abc", "z") = false
1417         * StringUtils.contains("abc", "A") = true
1418         * StringUtils.contains("abc", "Z") = false
1419         * </pre>
1420         *
1421         * @param str  the CharSequence to check, may be null
1422         * @param searchStr  the CharSequence to find, may be null
1423         * @return true if the CharSequence contains the search CharSequence irrespective of
1424         * case or false if not or {@code null} string input
1425         * @since 3.0 Changed signature from containsIgnoreCase(String, String) to containsIgnoreCase(CharSequence, CharSequence)
1426         */
1427        public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) {
1428            if (str == null || searchStr == null) {
1429                return false;
1430            }
1431            int len = searchStr.length();
1432            int max = str.length() - len;
1433            for (int i = 0; i <= max; i++) {
1434                if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) {
1435                    return true;
1436                }
1437            }
1438            return false;
1439        }
1440    
1441        /**
1442         * Check whether the given CharSequence contains any whitespace characters.
1443         * @param seq the CharSequence to check (may be {@code null})
1444         * @return {@code true} if the CharSequence is not empty and
1445         * contains at least 1 whitespace character
1446         * @see java.lang.Character#isWhitespace
1447         * @since 3.0
1448         */
1449        // From org.springframework.util.StringUtils, under Apache License 2.0
1450        public static boolean containsWhitespace(CharSequence seq) {
1451            if (isEmpty(seq)) {
1452                return false;
1453            }
1454            int strLen = seq.length();
1455            for (int i = 0; i < strLen; i++) {
1456                if (Character.isWhitespace(seq.charAt(i))) {
1457                    return true;
1458                }
1459            }
1460            return false;
1461        }
1462    
1463        // IndexOfAny chars
1464        //-----------------------------------------------------------------------
1465        /**
1466         * <p>Search a CharSequence to find the first index of any
1467         * character in the given set of characters.</p>
1468         *
1469         * <p>A {@code null} String will return {@code -1}.
1470         * A {@code null} or zero length search array will return {@code -1}.</p>
1471         *
1472         * <pre>
1473         * StringUtils.indexOfAny(null, *)                = -1
1474         * StringUtils.indexOfAny("", *)                  = -1
1475         * StringUtils.indexOfAny(*, null)                = -1
1476         * StringUtils.indexOfAny(*, [])                  = -1
1477         * StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0
1478         * StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3
1479         * StringUtils.indexOfAny("aba", ['z'])           = -1
1480         * </pre>
1481         *
1482         * @param cs  the CharSequence to check, may be null
1483         * @param searchChars  the chars to search for, may be null
1484         * @return the index of any of the chars, -1 if no match or null input
1485         * @since 2.0
1486         * @since 3.0 Changed signature from indexOfAny(String, char[]) to indexOfAny(CharSequence, char...)
1487         */
1488        public static int indexOfAny(CharSequence cs, char... searchChars) {
1489            if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
1490                return INDEX_NOT_FOUND;
1491            }
1492            int csLen = cs.length();
1493            int csLast = csLen - 1;
1494            int searchLen = searchChars.length;
1495            int searchLast = searchLen - 1;
1496            for (int i = 0; i < csLen; i++) {
1497                char ch = cs.charAt(i);
1498                for (int j = 0; j < searchLen; j++) {
1499                    if (searchChars[j] == ch) {
1500                        if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) {
1501                            // ch is a supplementary character
1502                            if (searchChars[j + 1] == cs.charAt(i + 1)) {
1503                                return i;
1504                            }
1505                        } else {
1506                            return i;
1507                        }
1508                    }
1509                }
1510            }
1511            return INDEX_NOT_FOUND;
1512        }
1513    
1514        /**
1515         * <p>Search a CharSequence to find the first index of any
1516         * character in the given set of characters.</p>
1517         *
1518         * <p>A {@code null} String will return {@code -1}.
1519         * A {@code null} search string will return {@code -1}.</p>
1520         *
1521         * <pre>
1522         * StringUtils.indexOfAny(null, *)            = -1
1523         * StringUtils.indexOfAny("", *)              = -1
1524         * StringUtils.indexOfAny(*, null)            = -1
1525         * StringUtils.indexOfAny(*, "")              = -1
1526         * StringUtils.indexOfAny("zzabyycdxx", "za") = 0
1527         * StringUtils.indexOfAny("zzabyycdxx", "by") = 3
1528         * StringUtils.indexOfAny("aba","z")          = -1
1529         * </pre>
1530         *
1531         * @param cs  the CharSequence to check, may be null
1532         * @param searchChars  the chars to search for, may be null
1533         * @return the index of any of the chars, -1 if no match or null input
1534         * @since 2.0
1535         * @since 3.0 Changed signature from indexOfAny(String, String) to indexOfAny(CharSequence, String)
1536         */
1537        public static int indexOfAny(CharSequence cs, String searchChars) {
1538            if (isEmpty(cs) || isEmpty(searchChars)) {
1539                return INDEX_NOT_FOUND;
1540            }
1541            return indexOfAny(cs, searchChars.toCharArray());
1542        }
1543    
1544        // ContainsAny
1545        //-----------------------------------------------------------------------
1546        /**
1547         * <p>Checks if the CharSequence contains any character in the given
1548         * set of characters.</p>
1549         *
1550         * <p>A {@code null} CharSequence will return {@code false}.
1551         * A {@code null} or zero length search array will return {@code false}.</p>
1552         *
1553         * <pre>
1554         * StringUtils.containsAny(null, *)                = false
1555         * StringUtils.containsAny("", *)                  = false
1556         * StringUtils.containsAny(*, null)                = false
1557         * StringUtils.containsAny(*, [])                  = false
1558         * StringUtils.containsAny("zzabyycdxx",['z','a']) = true
1559         * StringUtils.containsAny("zzabyycdxx",['b','y']) = true
1560         * StringUtils.containsAny("aba", ['z'])           = false
1561         * </pre>
1562         *
1563         * @param cs  the CharSequence to check, may be null
1564         * @param searchChars  the chars to search for, may be null
1565         * @return the {@code true} if any of the chars are found,
1566         * {@code false} if no match or null input
1567         * @since 2.4
1568         * @since 3.0 Changed signature from containsAny(String, char[]) to containsAny(CharSequence, char...)
1569         */
1570        public static boolean containsAny(CharSequence cs, char... searchChars) {
1571            if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
1572                return false;
1573            }
1574            int csLength = cs.length();
1575            int searchLength = searchChars.length;
1576            int csLast = csLength - 1;
1577            int searchLast = searchLength - 1;
1578            for (int i = 0; i < csLength; i++) {
1579                char ch = cs.charAt(i);
1580                for (int j = 0; j < searchLength; j++) {
1581                    if (searchChars[j] == ch) {
1582                        if (Character.isHighSurrogate(ch)) {
1583                            if (j == searchLast) {
1584                                // missing low surrogate, fine, like String.indexOf(String)
1585                                return true;
1586                            }
1587                            if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
1588                                return true;
1589                            }
1590                        } else {
1591                            // ch is in the Basic Multilingual Plane
1592                            return true;
1593                        }
1594                    }
1595                }
1596            }
1597            return false;
1598        }
1599    
1600        /**
1601         * <p>
1602         * Checks if the CharSequence contains any character in the given set of characters.
1603         * </p>
1604         *
1605         * <p>
1606         * A {@code null} CharSequence will return {@code false}. A {@code null} search CharSequence will return
1607         * {@code false}.
1608         * </p>
1609         *
1610         * <pre>
1611         * StringUtils.containsAny(null, *)            = false
1612         * StringUtils.containsAny("", *)              = false
1613         * StringUtils.containsAny(*, null)            = false
1614         * StringUtils.containsAny(*, "")              = false
1615         * StringUtils.containsAny("zzabyycdxx", "za") = true
1616         * StringUtils.containsAny("zzabyycdxx", "by") = true
1617         * StringUtils.containsAny("aba","z")          = false
1618         * </pre>
1619         *
1620         * @param cs
1621         *            the CharSequence to check, may be null
1622         * @param searchChars
1623         *            the chars to search for, may be null
1624         * @return the {@code true} if any of the chars are found, {@code false} if no match or null input
1625         * @since 2.4
1626         * @since 3.0 Changed signature from containsAny(String, String) to containsAny(CharSequence, CharSequence)
1627         */
1628        public static boolean containsAny(CharSequence cs, CharSequence searchChars) {
1629            if (searchChars == null) {
1630                return false;
1631            }
1632            return containsAny(cs, CharSequenceUtils.toCharArray(searchChars));
1633        }
1634    
1635        // IndexOfAnyBut chars
1636        //-----------------------------------------------------------------------
1637        /**
1638         * <p>Searches a CharSequence to find the first index of any
1639         * character not in the given set of characters.</p>
1640         *
1641         * <p>A {@code null} CharSequence will return {@code -1}.
1642         * A {@code null} or zero length search array will return {@code -1}.</p>
1643         *
1644         * <pre>
1645         * StringUtils.indexOfAnyBut(null, *)                              = -1
1646         * StringUtils.indexOfAnyBut("", *)                                = -1
1647         * StringUtils.indexOfAnyBut(*, null)                              = -1
1648         * StringUtils.indexOfAnyBut(*, [])                                = -1
1649         * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
1650         * StringUtils.indexOfAnyBut("aba", new char[] {'z'} )             = 0
1651         * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} )        = -1
1652    
1653         * </pre>
1654         *
1655         * @param cs  the CharSequence to check, may be null
1656         * @param searchChars  the chars to search for, may be null
1657         * @return the index of any of the chars, -1 if no match or null input
1658         * @since 2.0
1659         * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to indexOfAnyBut(CharSequence, char...)
1660         */
1661        public static int indexOfAnyBut(CharSequence cs, char... searchChars) {
1662            if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
1663                return INDEX_NOT_FOUND;
1664            }
1665            int csLen = cs.length();
1666            int csLast = csLen - 1;
1667            int searchLen = searchChars.length;
1668            int searchLast = searchLen - 1;
1669            outer:
1670            for (int i = 0; i < csLen; i++) {
1671                char ch = cs.charAt(i);
1672                for (int j = 0; j < searchLen; j++) {
1673                    if (searchChars[j] == ch) {
1674                        if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) {
1675                            if (searchChars[j + 1] == cs.charAt(i + 1)) {
1676                                continue outer;
1677                            }
1678                        } else {
1679                            continue outer;
1680                        }
1681                    }
1682                }
1683                return i;
1684            }
1685            return INDEX_NOT_FOUND;
1686        }
1687    
1688        /**
1689         * <p>Search a CharSequence to find the first index of any
1690         * character not in the given set of characters.</p>
1691         *
1692         * <p>A {@code null} CharSequence will return {@code -1}.
1693         * A {@code null} or empty search string will return {@code -1}.</p>
1694         *
1695         * <pre>
1696         * StringUtils.indexOfAnyBut(null, *)            = -1
1697         * StringUtils.indexOfAnyBut("", *)              = -1
1698         * StringUtils.indexOfAnyBut(*, null)            = -1
1699         * StringUtils.indexOfAnyBut(*, "")              = -1
1700         * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3
1701         * StringUtils.indexOfAnyBut("zzabyycdxx", "")   = -1
1702         * StringUtils.indexOfAnyBut("aba","ab")         = -1
1703         * </pre>
1704         *
1705         * @param seq  the CharSequence to check, may be null
1706         * @param searchChars  the chars to search for, may be null
1707         * @return the index of any of the chars, -1 if no match or null input
1708         * @since 2.0
1709         * @since 3.0 Changed signature from indexOfAnyBut(String, String) to indexOfAnyBut(CharSequence, CharSequence)
1710         */
1711        public static int indexOfAnyBut(CharSequence seq, CharSequence searchChars) {
1712            if (isEmpty(seq) || isEmpty(searchChars)) {
1713                return INDEX_NOT_FOUND;
1714            }
1715            int strLen = seq.length();
1716            for (int i = 0; i < strLen; i++) {
1717                char ch = seq.charAt(i);
1718                boolean chFound = CharSequenceUtils.indexOf(searchChars, ch, 0) >= 0;
1719                if (i + 1 < strLen && Character.isHighSurrogate(ch)) {
1720                    char ch2 = seq.charAt(i + 1);
1721                    if (chFound && CharSequenceUtils.indexOf(searchChars, ch2, 0) < 0) {
1722                        return i;
1723                    }
1724                } else {
1725                    if (!chFound) {
1726                        return i;
1727                    }
1728                }
1729            }
1730            return INDEX_NOT_FOUND;
1731        }
1732    
1733        // ContainsOnly
1734        //-----------------------------------------------------------------------
1735        /**
1736         * <p>Checks if the CharSequence contains only certain characters.</p>
1737         *
1738         * <p>A {@code null} CharSequence will return {@code false}.
1739         * A {@code null} valid character array will return {@code false}.
1740         * An empty CharSequence (length()=0) always returns {@code true}.</p>
1741         *
1742         * <pre>
1743         * StringUtils.containsOnly(null, *)       = false
1744         * StringUtils.containsOnly(*, null)       = false
1745         * StringUtils.containsOnly("", *)         = true
1746         * StringUtils.containsOnly("ab", '')      = false
1747         * StringUtils.containsOnly("abab", 'abc') = true
1748         * StringUtils.containsOnly("ab1", 'abc')  = false
1749         * StringUtils.containsOnly("abz", 'abc')  = false
1750         * </pre>
1751         *
1752         * @param cs  the String to check, may be null
1753         * @param valid  an array of valid chars, may be null
1754         * @return true if it only contains valid chars and is non-null
1755         * @since 3.0 Changed signature from containsOnly(String, char[]) to containsOnly(CharSequence, char...)
1756         */
1757        public static boolean containsOnly(CharSequence cs, char... valid) {
1758            // All these pre-checks are to maintain API with an older version
1759            if (valid == null || cs == null) {
1760                return false;
1761            }
1762            if (cs.length() == 0) {
1763                return true;
1764            }
1765            if (valid.length == 0) {
1766                return false;
1767            }
1768            return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND;
1769        }
1770    
1771        /**
1772         * <p>Checks if the CharSequence contains only certain characters.</p>
1773         *
1774         * <p>A {@code null} CharSequence will return {@code false}.
1775         * A {@code null} valid character String will return {@code false}.
1776         * An empty String (length()=0) always returns {@code true}.</p>
1777         *
1778         * <pre>
1779         * StringUtils.containsOnly(null, *)       = false
1780         * StringUtils.containsOnly(*, null)       = false
1781         * StringUtils.containsOnly("", *)         = true
1782         * StringUtils.containsOnly("ab", "")      = false
1783         * StringUtils.containsOnly("abab", "abc") = true
1784         * StringUtils.containsOnly("ab1", "abc")  = false
1785         * StringUtils.containsOnly("abz", "abc")  = false
1786         * </pre>
1787         *
1788         * @param cs  the CharSequence to check, may be null
1789         * @param validChars  a String of valid chars, may be null
1790         * @return true if it only contains valid chars and is non-null
1791         * @since 2.0
1792         * @since 3.0 Changed signature from containsOnly(String, String) to containsOnly(CharSequence, String)
1793         */
1794        public static boolean containsOnly(CharSequence cs, String validChars) {
1795            if (cs == null || validChars == null) {
1796                return false;
1797            }
1798            return containsOnly(cs, validChars.toCharArray());
1799        }
1800    
1801        // ContainsNone
1802        //-----------------------------------------------------------------------
1803        /**
1804         * <p>Checks that the CharSequence does not contain certain characters.</p>
1805         *
1806         * <p>A {@code null} CharSequence will return {@code true}.
1807         * A {@code null} invalid character array will return {@code true}.
1808         * An empty CharSequence (length()=0) always returns true.</p>
1809         *
1810         * <pre>
1811         * StringUtils.containsNone(null, *)       = true
1812         * StringUtils.containsNone(*, null)       = true
1813         * StringUtils.containsNone("", *)         = true
1814         * StringUtils.containsNone("ab", '')      = true
1815         * StringUtils.containsNone("abab", 'xyz') = true
1816         * StringUtils.containsNone("ab1", 'xyz')  = true
1817         * StringUtils.containsNone("abz", 'xyz')  = false
1818         * </pre>
1819         *
1820         * @param cs  the CharSequence to check, may be null
1821         * @param searchChars  an array of invalid chars, may be null
1822         * @return true if it contains none of the invalid chars, or is null
1823         * @since 2.0
1824         * @since 3.0 Changed signature from containsNone(String, char[]) to containsNone(CharSequence, char...)
1825         */
1826        public static boolean containsNone(CharSequence cs, char... searchChars) {
1827            if (cs == null || searchChars == null) {
1828                return true;
1829            }
1830            int csLen = cs.length();
1831            int csLast = csLen - 1;
1832            int searchLen = searchChars.length;
1833            int searchLast = searchLen - 1;
1834            for (int i = 0; i < csLen; i++) {
1835                char ch = cs.charAt(i);
1836                for (int j = 0; j < searchLen; j++) {
1837                    if (searchChars[j] == ch) {
1838                        if (Character.isHighSurrogate(ch)) {
1839                            if (j == searchLast) {
1840                                // missing low surrogate, fine, like String.indexOf(String)
1841                                return false;
1842                            }
1843                            if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
1844                                return false;
1845                            }
1846                        } else {
1847                            // ch is in the Basic Multilingual Plane
1848                            return false;
1849                        }
1850                    }
1851                }
1852            }
1853            return true;
1854        }
1855    
1856        /**
1857         * <p>Checks that the CharSequence does not contain certain characters.</p>
1858         *
1859         * <p>A {@code null} CharSequence will return {@code true}.
1860         * A {@code null} invalid character array will return {@code true}.
1861         * An empty String ("") always returns true.</p>
1862         *
1863         * <pre>
1864         * StringUtils.containsNone(null, *)       = true
1865         * StringUtils.containsNone(*, null)       = true
1866         * StringUtils.containsNone("", *)         = true
1867         * StringUtils.containsNone("ab", "")      = true
1868         * StringUtils.containsNone("abab", "xyz") = true
1869         * StringUtils.containsNone("ab1", "xyz")  = true
1870         * StringUtils.containsNone("abz", "xyz")  = false
1871         * </pre>
1872         *
1873         * @param cs  the CharSequence to check, may be null
1874         * @param invalidChars  a String of invalid chars, may be null
1875         * @return true if it contains none of the invalid chars, or is null
1876         * @since 2.0
1877         * @since 3.0 Changed signature from containsNone(String, String) to containsNone(CharSequence, String)
1878         */
1879        public static boolean containsNone(CharSequence cs, String invalidChars) {
1880            if (cs == null || invalidChars == null) {
1881                return true;
1882            }
1883            return containsNone(cs, invalidChars.toCharArray());
1884        }
1885    
1886        // IndexOfAny strings
1887        //-----------------------------------------------------------------------
1888        /**
1889         * <p>Find the first index of any of a set of potential substrings.</p>
1890         *
1891         * <p>A {@code null} CharSequence will return {@code -1}.
1892         * A {@code null} or zero length search array will return {@code -1}.
1893         * A {@code null} search array entry will be ignored, but a search
1894         * array containing "" will return {@code 0} if {@code str} is not
1895         * null. This method uses {@link String#indexOf(String)} if possible.</p>
1896         *
1897         * <pre>
1898         * StringUtils.indexOfAny(null, *)                     = -1
1899         * StringUtils.indexOfAny(*, null)                     = -1
1900         * StringUtils.indexOfAny(*, [])                       = -1
1901         * StringUtils.indexOfAny("zzabyycdxx", ["ab","cd"])   = 2
1902         * StringUtils.indexOfAny("zzabyycdxx", ["cd","ab"])   = 2
1903         * StringUtils.indexOfAny("zzabyycdxx", ["mn","op"])   = -1
1904         * StringUtils.indexOfAny("zzabyycdxx", ["zab","aby"]) = 1
1905         * StringUtils.indexOfAny("zzabyycdxx", [""])          = 0
1906         * StringUtils.indexOfAny("", [""])                    = 0
1907         * StringUtils.indexOfAny("", ["a"])                   = -1
1908         * </pre>
1909         *
1910         * @param str  the CharSequence to check, may be null
1911         * @param searchStrs  the CharSequences to search for, may be null
1912         * @return the first index of any of the searchStrs in str, -1 if no match
1913         * @since 3.0 Changed signature from indexOfAny(String, String[]) to indexOfAny(CharSequence, CharSequence...)
1914         */
1915        public static int indexOfAny(CharSequence str, CharSequence... searchStrs) {
1916            if (str == null || searchStrs == null) {
1917                return INDEX_NOT_FOUND;
1918            }
1919            int sz = searchStrs.length;
1920    
1921            // String's can't have a MAX_VALUEth index.
1922            int ret = Integer.MAX_VALUE;
1923    
1924            int tmp = 0;
1925            for (int i = 0; i < sz; i++) {
1926                CharSequence search = searchStrs[i];
1927                if (search == null) {
1928                    continue;
1929                }
1930                tmp = CharSequenceUtils.indexOf(str, search, 0);
1931                if (tmp == INDEX_NOT_FOUND) {
1932                    continue;
1933                }
1934    
1935                if (tmp < ret) {
1936                    ret = tmp;
1937                }
1938            }
1939    
1940            return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret;
1941        }
1942    
1943        /**
1944         * <p>Find the latest index of any of a set of potential substrings.</p>
1945         *
1946         * <p>A {@code null} CharSequence will return {@code -1}.
1947         * A {@code null} search array will return {@code -1}.
1948         * A {@code null} or zero length search array entry will be ignored,
1949         * but a search array containing "" will return the length of {@code str}
1950         * if {@code str} is not null. This method uses {@link String#indexOf(String)} if possible</p>
1951         *
1952         * <pre>
1953         * StringUtils.lastIndexOfAny(null, *)                   = -1
1954         * StringUtils.lastIndexOfAny(*, null)                   = -1
1955         * StringUtils.lastIndexOfAny(*, [])                     = -1
1956         * StringUtils.lastIndexOfAny(*, [null])                 = -1
1957         * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6
1958         * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6
1959         * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
1960         * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
1961         * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""])   = 10
1962         * </pre>
1963         *
1964         * @param str  the CharSequence to check, may be null
1965         * @param searchStrs  the CharSequences to search for, may be null
1966         * @return the last index of any of the CharSequences, -1 if no match
1967         * @since 3.0 Changed signature from lastIndexOfAny(String, String[]) to lastIndexOfAny(CharSequence, CharSequence)
1968         */
1969        public static int lastIndexOfAny(CharSequence str, CharSequence... searchStrs) {
1970            if (str == null || searchStrs == null) {
1971                return INDEX_NOT_FOUND;
1972            }
1973            int sz = searchStrs.length;
1974            int ret = INDEX_NOT_FOUND;
1975            int tmp = 0;
1976            for (int i = 0; i < sz; i++) {
1977                CharSequence search = searchStrs[i];
1978                if (search == null) {
1979                    continue;
1980                }
1981                tmp = CharSequenceUtils.lastIndexOf(str, search, str.length());
1982                if (tmp > ret) {
1983                    ret = tmp;
1984                }
1985            }
1986            return ret;
1987        }
1988    
1989        // Substring
1990        //-----------------------------------------------------------------------
1991        /**
1992         * <p>Gets a substring from the specified String avoiding exceptions.</p>
1993         *
1994         * <p>A negative start position can be used to start {@code n}
1995         * characters from the end of the String.</p>
1996         *
1997         * <p>A {@code null} String will return {@code null}.
1998         * An empty ("") String will return "".</p>
1999         *
2000         * <pre>
2001         * StringUtils.substring(null, *)   = null
2002         * StringUtils.substring("", *)     = ""
2003         * StringUtils.substring("abc", 0)  = "abc"
2004         * StringUtils.substring("abc", 2)  = "c"
2005         * StringUtils.substring("abc", 4)  = ""
2006         * StringUtils.substring("abc", -2) = "bc"
2007         * StringUtils.substring("abc", -4) = "abc"
2008         * </pre>
2009         *
2010         * @param str  the String to get the substring from, may be null
2011         * @param start  the position to start from, negative means
2012         *  count back from the end of the String by this many characters
2013         * @return substring from start position, {@code null} if null String input
2014         */
2015        public static String substring(String str, int start) {
2016            if (str == null) {
2017                return null;
2018            }
2019    
2020            // handle negatives, which means last n characters
2021            if (start < 0) {
2022                start = str.length() + start; // remember start is negative
2023            }
2024    
2025            if (start < 0) {
2026                start = 0;
2027            }
2028            if (start > str.length()) {
2029                return EMPTY;
2030            }
2031    
2032            return str.substring(start);
2033        }
2034    
2035        /**
2036         * <p>Gets a substring from the specified String avoiding exceptions.</p>
2037         *
2038         * <p>A negative start position can be used to start/end {@code n}
2039         * characters from the end of the String.</p>
2040         *
2041         * <p>The returned substring starts with the character in the {@code start}
2042         * position and ends before the {@code end} position. All position counting is
2043         * zero-based -- i.e., to start at the beginning of the string use
2044         * {@code start = 0}. Negative start and end positions can be used to
2045         * specify offsets relative to the end of the String.</p>
2046         *
2047         * <p>If {@code start} is not strictly to the left of {@code end}, ""
2048         * is returned.</p>
2049         *
2050         * <pre>
2051         * StringUtils.substring(null, *, *)    = null
2052         * StringUtils.substring("", * ,  *)    = "";
2053         * StringUtils.substring("abc", 0, 2)   = "ab"
2054         * StringUtils.substring("abc", 2, 0)   = ""
2055         * StringUtils.substring("abc", 2, 4)   = "c"
2056         * StringUtils.substring("abc", 4, 6)   = ""
2057         * StringUtils.substring("abc", 2, 2)   = ""
2058         * StringUtils.substring("abc", -2, -1) = "b"
2059         * StringUtils.substring("abc", -4, 2)  = "ab"
2060         * </pre>
2061         *
2062         * @param str  the String to get the substring from, may be null
2063         * @param start  the position to start from, negative means
2064         *  count back from the end of the String by this many characters
2065         * @param end  the position to end at (exclusive), negative means
2066         *  count back from the end of the String by this many characters
2067         * @return substring from start position to end position,
2068         *  {@code null} if null String input
2069         */
2070        public static String substring(String str, int start, int end) {
2071            if (str == null) {
2072                return null;
2073            }
2074    
2075            // handle negatives
2076            if (end < 0) {
2077                end = str.length() + end; // remember end is negative
2078            }
2079            if (start < 0) {
2080                start = str.length() + start; // remember start is negative
2081            }
2082    
2083            // check length next
2084            if (end > str.length()) {
2085                end = str.length();
2086            }
2087    
2088            // if start is greater than end, return ""
2089            if (start > end) {
2090                return EMPTY;
2091            }
2092    
2093            if (start < 0) {
2094                start = 0;
2095            }
2096            if (end < 0) {
2097                end = 0;
2098            }
2099    
2100            return str.substring(start, end);
2101        }
2102    
2103        // Left/Right/Mid
2104        //-----------------------------------------------------------------------
2105        /**
2106         * <p>Gets the leftmost {@code len} characters of a String.</p>
2107         *
2108         * <p>If {@code len} characters are not available, or the
2109         * String is {@code null}, the String will be returned without
2110         * an exception. An empty String is returned if len is negative.</p>
2111         *
2112         * <pre>
2113         * StringUtils.left(null, *)    = null
2114         * StringUtils.left(*, -ve)     = ""
2115         * StringUtils.left("", *)      = ""
2116         * StringUtils.left("abc", 0)   = ""
2117         * StringUtils.left("abc", 2)   = "ab"
2118         * StringUtils.left("abc", 4)   = "abc"
2119         * </pre>
2120         *
2121         * @param str  the String to get the leftmost characters from, may be null
2122         * @param len  the length of the required String
2123         * @return the leftmost characters, {@code null} if null String input
2124         */
2125        public static String left(String str, int len) {
2126            if (str == null) {
2127                return null;
2128            }
2129            if (len < 0) {
2130                return EMPTY;
2131            }
2132            if (str.length() <= len) {
2133                return str;
2134            }
2135            return str.substring(0, len);
2136        }
2137    
2138        /**
2139         * <p>Gets the rightmost {@code len} characters of a String.</p>
2140         *
2141         * <p>If {@code len} characters are not available, or the String
2142         * is {@code null}, the String will be returned without an
2143         * an exception. An empty String is returned if len is negative.</p>
2144         *
2145         * <pre>
2146         * StringUtils.right(null, *)    = null
2147         * StringUtils.right(*, -ve)     = ""
2148         * StringUtils.right("", *)      = ""
2149         * StringUtils.right("abc", 0)   = ""
2150         * StringUtils.right("abc", 2)   = "bc"
2151         * StringUtils.right("abc", 4)   = "abc"
2152         * </pre>
2153         *
2154         * @param str  the String to get the rightmost characters from, may be null
2155         * @param len  the length of the required String
2156         * @return the rightmost characters, {@code null} if null String input
2157         */
2158        public static String right(String str, int len) {
2159            if (str == null) {
2160                return null;
2161            }
2162            if (len < 0) {
2163                return EMPTY;
2164            }
2165            if (str.length() <= len) {
2166                return str;
2167            }
2168            return str.substring(str.length() - len);
2169        }
2170    
2171        /**
2172         * <p>Gets {@code len} characters from the middle of a String.</p>
2173         *
2174         * <p>If {@code len} characters are not available, the remainder
2175         * of the String will be returned without an exception. If the
2176         * String is {@code null}, {@code null} will be returned.
2177         * An empty String is returned if len is negative or exceeds the
2178         * length of {@code str}.</p>
2179         *
2180         * <pre>
2181         * StringUtils.mid(null, *, *)    = null
2182         * StringUtils.mid(*, *, -ve)     = ""
2183         * StringUtils.mid("", 0, *)      = ""
2184         * StringUtils.mid("abc", 0, 2)   = "ab"
2185         * StringUtils.mid("abc", 0, 4)   = "abc"
2186         * StringUtils.mid("abc", 2, 4)   = "c"
2187         * StringUtils.mid("abc", 4, 2)   = ""
2188         * StringUtils.mid("abc", -2, 2)  = "ab"
2189         * </pre>
2190         *
2191         * @param str  the String to get the characters from, may be null
2192         * @param pos  the position to start from, negative treated as zero
2193         * @param len  the length of the required String
2194         * @return the middle characters, {@code null} if null String input
2195         */
2196        public static String mid(String str, int pos, int len) {
2197            if (str == null) {
2198                return null;
2199            }
2200            if (len < 0 || pos > str.length()) {
2201                return EMPTY;
2202            }
2203            if (pos < 0) {
2204                pos = 0;
2205            }
2206            if (str.length() <= (pos + len)) {
2207                return str.substring(pos);
2208            }
2209            return str.substring(pos, pos + len);
2210        }
2211    
2212        // SubStringAfter/SubStringBefore
2213        //-----------------------------------------------------------------------
2214        /**
2215         * <p>Gets the substring before the first occurrence of a separator.
2216         * The separator is not returned.</p>
2217         *
2218         * <p>A {@code null} string input will return {@code null}.
2219         * An empty ("") string input will return the empty string.
2220         * A {@code null} separator will return the input string.</p>
2221         *
2222         * <p>If nothing is found, the string input is returned.</p>
2223         *
2224         * <pre>
2225         * StringUtils.substringBefore(null, *)      = null
2226         * StringUtils.substringBefore("", *)        = ""
2227         * StringUtils.substringBefore("abc", "a")   = ""
2228         * StringUtils.substringBefore("abcba", "b") = "a"
2229         * StringUtils.substringBefore("abc", "c")   = "ab"
2230         * StringUtils.substringBefore("abc", "d")   = "abc"
2231         * StringUtils.substringBefore("abc", "")    = ""
2232         * StringUtils.substringBefore("abc", null)  = "abc"
2233         * </pre>
2234         *
2235         * @param str  the String to get a substring from, may be null
2236         * @param separator  the String to search for, may be null
2237         * @return the substring before the first occurrence of the separator,
2238         *  {@code null} if null String input
2239         * @since 2.0
2240         */
2241        public static String substringBefore(String str, String separator) {
2242            if (isEmpty(str) || separator == null) {
2243                return str;
2244            }
2245            if (separator.length() == 0) {
2246                return EMPTY;
2247            }
2248            int pos = str.indexOf(separator);
2249            if (pos == INDEX_NOT_FOUND) {
2250                return str;
2251            }
2252            return str.substring(0, pos);
2253        }
2254    
2255        /**
2256         * <p>Gets the substring after the first occurrence of a separator.
2257         * The separator is not returned.</p>
2258         *
2259         * <p>A {@code null} string input will return {@code null}.
2260         * An empty ("") string input will return the empty string.
2261         * A {@code null} separator will return the empty string if the
2262         * input string is not {@code null}.</p>
2263         *
2264         * <p>If nothing is found, the empty string is returned.</p>
2265         *
2266         * <pre>
2267         * StringUtils.substringAfter(null, *)      = null
2268         * StringUtils.substringAfter("", *)        = ""
2269         * StringUtils.substringAfter(*, null)      = ""
2270         * StringUtils.substringAfter("abc", "a")   = "bc"
2271         * StringUtils.substringAfter("abcba", "b") = "cba"
2272         * StringUtils.substringAfter("abc", "c")   = ""
2273         * StringUtils.substringAfter("abc", "d")   = ""
2274         * StringUtils.substringAfter("abc", "")    = "abc"
2275         * </pre>
2276         *
2277         * @param str  the String to get a substring from, may be null
2278         * @param separator  the String to search for, may be null
2279         * @return the substring after the first occurrence of the separator,
2280         *  {@code null} if null String input
2281         * @since 2.0
2282         */
2283        public static String substringAfter(String str, String separator) {
2284            if (isEmpty(str)) {
2285                return str;
2286            }
2287            if (separator == null) {
2288                return EMPTY;
2289            }
2290            int pos = str.indexOf(separator);
2291            if (pos == INDEX_NOT_FOUND) {
2292                return EMPTY;
2293            }
2294            return str.substring(pos + separator.length());
2295        }
2296    
2297        /**
2298         * <p>Gets the substring before the last occurrence of a separator.
2299         * The separator is not returned.</p>
2300         *
2301         * <p>A {@code null} string input will return {@code null}.
2302         * An empty ("") string input will return the empty string.
2303         * An empty or {@code null} separator will return the input string.</p>
2304         *
2305         * <p>If nothing is found, the string input is returned.</p>
2306         *
2307         * <pre>
2308         * StringUtils.substringBeforeLast(null, *)      = null
2309         * StringUtils.substringBeforeLast("", *)        = ""
2310         * StringUtils.substringBeforeLast("abcba", "b") = "abc"
2311         * StringUtils.substringBeforeLast("abc", "c")   = "ab"
2312         * StringUtils.substringBeforeLast("a", "a")     = ""
2313         * StringUtils.substringBeforeLast("a", "z")     = "a"
2314         * StringUtils.substringBeforeLast("a", null)    = "a"
2315         * StringUtils.substringBeforeLast("a", "")      = "a"
2316         * </pre>
2317         *
2318         * @param str  the String to get a substring from, may be null
2319         * @param separator  the String to search for, may be null
2320         * @return the substring before the last occurrence of the separator,
2321         *  {@code null} if null String input
2322         * @since 2.0
2323         */
2324        public static String substringBeforeLast(String str, String separator) {
2325            if (isEmpty(str) || isEmpty(separator)) {
2326                return str;
2327            }
2328            int pos = str.lastIndexOf(separator);
2329            if (pos == INDEX_NOT_FOUND) {
2330                return str;
2331            }
2332            return str.substring(0, pos);
2333        }
2334    
2335        /**
2336         * <p>Gets the substring after the last occurrence of a separator.
2337         * The separator is not returned.</p>
2338         *
2339         * <p>A {@code null} string input will return {@code null}.
2340         * An empty ("") string input will return the empty string.
2341         * An empty or {@code null} separator will return the empty string if
2342         * the input string is not {@code null}.</p>
2343         *
2344         * <p>If nothing is found, the empty string is returned.</p>
2345         *
2346         * <pre>
2347         * StringUtils.substringAfterLast(null, *)      = null
2348         * StringUtils.substringAfterLast("", *)        = ""
2349         * StringUtils.substringAfterLast(*, "")        = ""
2350         * StringUtils.substringAfterLast(*, null)      = ""
2351         * StringUtils.substringAfterLast("abc", "a")   = "bc"
2352         * StringUtils.substringAfterLast("abcba", "b") = "a"
2353         * StringUtils.substringAfterLast("abc", "c")   = ""
2354         * StringUtils.substringAfterLast("a", "a")     = ""
2355         * StringUtils.substringAfterLast("a", "z")     = ""
2356         * </pre>
2357         *
2358         * @param str  the String to get a substring from, may be null
2359         * @param separator  the String to search for, may be null
2360         * @return the substring after the last occurrence of the separator,
2361         *  {@code null} if null String input
2362         * @since 2.0
2363         */
2364        public static String substringAfterLast(String str, String separator) {
2365            if (isEmpty(str)) {
2366                return str;
2367            }
2368            if (isEmpty(separator)) {
2369                return EMPTY;
2370            }
2371            int pos = str.lastIndexOf(separator);
2372            if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) {
2373                return EMPTY;
2374            }
2375            return str.substring(pos + separator.length());
2376        }
2377    
2378        // Substring between
2379        //-----------------------------------------------------------------------
2380        /**
2381         * <p>Gets the String that is nested in between two instances of the
2382         * same String.</p>
2383         *
2384         * <p>A {@code null} input String returns {@code null}.
2385         * A {@code null} tag returns {@code null}.</p>
2386         *
2387         * <pre>
2388         * StringUtils.substringBetween(null, *)            = null
2389         * StringUtils.substringBetween("", "")             = ""
2390         * StringUtils.substringBetween("", "tag")          = null
2391         * StringUtils.substringBetween("tagabctag", null)  = null
2392         * StringUtils.substringBetween("tagabctag", "")    = ""
2393         * StringUtils.substringBetween("tagabctag", "tag") = "abc"
2394         * </pre>
2395         *
2396         * @param str  the String containing the substring, may be null
2397         * @param tag  the String before and after the substring, may be null
2398         * @return the substring, {@code null} if no match
2399         * @since 2.0
2400         */
2401        public static String substringBetween(String str, String tag) {
2402            return substringBetween(str, tag, tag);
2403        }
2404    
2405        /**
2406         * <p>Gets the String that is nested in between two Strings.
2407         * Only the first match is returned.</p>
2408         *
2409         * <p>A {@code null} input String returns {@code null}.
2410         * A {@code null} open/close returns {@code null} (no match).
2411         * An empty ("") open and close returns an empty string.</p>
2412         *
2413         * <pre>
2414         * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
2415         * StringUtils.substringBetween(null, *, *)          = null
2416         * StringUtils.substringBetween(*, null, *)          = null
2417         * StringUtils.substringBetween(*, *, null)          = null
2418         * StringUtils.substringBetween("", "", "")          = ""
2419         * StringUtils.substringBetween("", "", "]")         = null
2420         * StringUtils.substringBetween("", "[", "]")        = null
2421         * StringUtils.substringBetween("yabcz", "", "")     = ""
2422         * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
2423         * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
2424         * </pre>
2425         *
2426         * @param str  the String containing the substring, may be null
2427         * @param open  the String before the substring, may be null
2428         * @param close  the String after the substring, may be null
2429         * @return the substring, {@code null} if no match
2430         * @since 2.0
2431         */
2432        public static String substringBetween(String str, String open, String close) {
2433            if (str == null || open == null || close == null) {
2434                return null;
2435            }
2436            int start = str.indexOf(open);
2437            if (start != INDEX_NOT_FOUND) {
2438                int end = str.indexOf(close, start + open.length());
2439                if (end != INDEX_NOT_FOUND) {
2440                    return str.substring(start + open.length(), end);
2441                }
2442            }
2443            return null;
2444        }
2445    
2446        /**
2447         * <p>Searches a String for substrings delimited by a start and end tag,
2448         * returning all matching substrings in an array.</p>
2449         *
2450         * <p>A {@code null} input String returns {@code null}.
2451         * A {@code null} open/close returns {@code null} (no match).
2452         * An empty ("") open/close returns {@code null} (no match).</p>
2453         *
2454         * <pre>
2455         * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
2456         * StringUtils.substringsBetween(null, *, *)            = null
2457         * StringUtils.substringsBetween(*, null, *)            = null
2458         * StringUtils.substringsBetween(*, *, null)            = null
2459         * StringUtils.substringsBetween("", "[", "]")          = []
2460         * </pre>
2461         *
2462         * @param str  the String containing the substrings, null returns null, empty returns empty
2463         * @param open  the String identifying the start of the substring, empty returns null
2464         * @param close  the String identifying the end of the substring, empty returns null
2465         * @return a String Array of substrings, or {@code null} if no match
2466         * @since 2.3
2467         */
2468        public static String[] substringsBetween(String str, String open, String close) {
2469            if (str == null || isEmpty(open) || isEmpty(close)) {
2470                return null;
2471            }
2472            int strLen = str.length();
2473            if (strLen == 0) {
2474                return ArrayUtils.EMPTY_STRING_ARRAY;
2475            }
2476            int closeLen = close.length();
2477            int openLen = open.length();
2478            List<String> list = new ArrayList<String>();
2479            int pos = 0;
2480            while (pos < (strLen - closeLen)) {
2481                int start = str.indexOf(open, pos);
2482                if (start < 0) {
2483                    break;
2484                }
2485                start += openLen;
2486                int end = str.indexOf(close, start);
2487                if (end < 0) {
2488                    break;
2489                }
2490                list.add(str.substring(start, end));
2491                pos = end + closeLen;
2492            }
2493            if (list.isEmpty()) {
2494                return null;
2495            }
2496            return list.toArray(new String [list.size()]);
2497        }
2498    
2499        // Nested extraction
2500        //-----------------------------------------------------------------------
2501    
2502        // Splitting
2503        //-----------------------------------------------------------------------
2504        /**
2505         * <p>Splits the provided text into an array, using whitespace as the
2506         * separator.
2507         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
2508         *
2509         * <p>The separator is not included in the returned String array.
2510         * Adjacent separators are treated as one separator.
2511         * For more control over the split use the StrTokenizer class.</p>
2512         *
2513         * <p>A {@code null} input String returns {@code null}.</p>
2514         *
2515         * <pre>
2516         * StringUtils.split(null)       = null
2517         * StringUtils.split("")         = []
2518         * StringUtils.split("abc def")  = ["abc", "def"]
2519         * StringUtils.split("abc  def") = ["abc", "def"]
2520         * StringUtils.split(" abc ")    = ["abc"]
2521         * </pre>
2522         *
2523         * @param str  the String to parse, may be null
2524         * @return an array of parsed Strings, {@code null} if null String input
2525         */
2526        public static String[] split(String str) {
2527            return split(str, null, -1);
2528        }
2529    
2530        /**
2531         * <p>Splits the provided text into an array, separator specified.
2532         * This is an alternative to using StringTokenizer.</p>
2533         *
2534         * <p>The separator is not included in the returned String array.
2535         * Adjacent separators are treated as one separator.
2536         * For more control over the split use the StrTokenizer class.</p>
2537         *
2538         * <p>A {@code null} input String returns {@code null}.</p>
2539         *
2540         * <pre>
2541         * StringUtils.split(null, *)         = null
2542         * StringUtils.split("", *)           = []
2543         * StringUtils.split("a.b.c", '.')    = ["a", "b", "c"]
2544         * StringUtils.split("a..b.c", '.')   = ["a", "b", "c"]
2545         * StringUtils.split("a:b:c", '.')    = ["a:b:c"]
2546         * StringUtils.split("a b c", ' ')    = ["a", "b", "c"]
2547         * </pre>
2548         *
2549         * @param str  the String to parse, may be null
2550         * @param separatorChar  the character used as the delimiter
2551         * @return an array of parsed Strings, {@code null} if null String input
2552         * @since 2.0
2553         */
2554        public static String[] split(String str, char separatorChar) {
2555            return splitWorker(str, separatorChar, false);
2556        }
2557    
2558        /**
2559         * <p>Splits the provided text into an array, separators specified.
2560         * This is an alternative to using StringTokenizer.</p>
2561         *
2562         * <p>The separator is not included in the returned String array.
2563         * Adjacent separators are treated as one separator.
2564         * For more control over the split use the StrTokenizer class.</p>
2565         *
2566         * <p>A {@code null} input String returns {@code null}.
2567         * A {@code null} separatorChars splits on whitespace.</p>
2568         *
2569         * <pre>
2570         * StringUtils.split(null, *)         = null
2571         * StringUtils.split("", *)           = []
2572         * StringUtils.split("abc def", null) = ["abc", "def"]
2573         * StringUtils.split("abc def", " ")  = ["abc", "def"]
2574         * StringUtils.split("abc  def", " ") = ["abc", "def"]
2575         * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
2576         * </pre>
2577         *
2578         * @param str  the String to parse, may be null
2579         * @param separatorChars  the characters used as the delimiters,
2580         *  {@code null} splits on whitespace
2581         * @return an array of parsed Strings, {@code null} if null String input
2582         */
2583        public static String[] split(String str, String separatorChars) {
2584            return splitWorker(str, separatorChars, -1, false);
2585        }
2586    
2587        /**
2588         * <p>Splits the provided text into an array with a maximum length,
2589         * separators specified.</p>
2590         *
2591         * <p>The separator is not included in the returned String array.
2592         * Adjacent separators are treated as one separator.</p>
2593         *
2594         * <p>A {@code null} input String returns {@code null}.
2595         * A {@code null} separatorChars splits on whitespace.</p>
2596         *
2597         * <p>If more than {@code max} delimited substrings are found, the last
2598         * returned string includes all characters after the first {@code max - 1}
2599         * returned strings (including separator characters).</p>
2600         *
2601         * <pre>
2602         * StringUtils.split(null, *, *)            = null
2603         * StringUtils.split("", *, *)              = []
2604         * StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
2605         * StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
2606         * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
2607         * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
2608         * </pre>
2609         *
2610         * @param str  the String to parse, may be null
2611         * @param separatorChars  the characters used as the delimiters,
2612         *  {@code null} splits on whitespace
2613         * @param max  the maximum number of elements to include in the
2614         *  array. A zero or negative value implies no limit
2615         * @return an array of parsed Strings, {@code null} if null String input
2616         */
2617        public static String[] split(String str, String separatorChars, int max) {
2618            return splitWorker(str, separatorChars, max, false);
2619        }
2620    
2621        /**
2622         * <p>Splits the provided text into an array, separator string specified.</p>
2623         *
2624         * <p>The separator(s) will not be included in the returned String array.
2625         * Adjacent separators are treated as one separator.</p>
2626         *
2627         * <p>A {@code null} input String returns {@code null}.
2628         * A {@code null} separator splits on whitespace.</p>
2629         *
2630         * <pre>
2631         * StringUtils.splitByWholeSeparator(null, *)               = null
2632         * StringUtils.splitByWholeSeparator("", *)                 = []
2633         * StringUtils.splitByWholeSeparator("ab de fg", null)      = ["ab", "de", "fg"]
2634         * StringUtils.splitByWholeSeparator("ab   de fg", null)    = ["ab", "de", "fg"]
2635         * StringUtils.splitByWholeSeparator("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
2636         * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
2637         * </pre>
2638         *
2639         * @param str  the String to parse, may be null
2640         * @param separator  String containing the String to be used as a delimiter,
2641         *  {@code null} splits on whitespace
2642         * @return an array of parsed Strings, {@code null} if null String was input
2643         */
2644        public static String[] splitByWholeSeparator(String str, String separator) {
2645            return splitByWholeSeparatorWorker( str, separator, -1, false ) ;
2646        }
2647    
2648        /**
2649         * <p>Splits the provided text into an array, separator string specified.
2650         * Returns a maximum of {@code max} substrings.</p>
2651         *
2652         * <p>The separator(s) will not be included in the returned String array.
2653         * Adjacent separators are treated as one separator.</p>
2654         *
2655         * <p>A {@code null} input String returns {@code null}.
2656         * A {@code null} separator splits on whitespace.</p>
2657         *
2658         * <pre>
2659         * StringUtils.splitByWholeSeparator(null, *, *)               = null
2660         * StringUtils.splitByWholeSeparator("", *, *)                 = []
2661         * StringUtils.splitByWholeSeparator("ab de fg", null, 0)      = ["ab", "de", "fg"]
2662         * StringUtils.splitByWholeSeparator("ab   de fg", null, 0)    = ["ab", "de", "fg"]
2663         * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
2664         * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
2665         * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
2666         * </pre>
2667         *
2668         * @param str  the String to parse, may be null
2669         * @param separator  String containing the String to be used as a delimiter,
2670         *  {@code null} splits on whitespace
2671         * @param max  the maximum number of elements to include in the returned
2672         *  array. A zero or negative value implies no limit.
2673         * @return an array of parsed Strings, {@code null} if null String was input
2674         */
2675        public static String[] splitByWholeSeparator( String str, String separator, int max ) {
2676            return splitByWholeSeparatorWorker(str, separator, max, false);
2677        }
2678    
2679        /**
2680         * <p>Splits the provided text into an array, separator string specified. </p>
2681         *
2682         * <p>The separator is not included in the returned String array.
2683         * Adjacent separators are treated as separators for empty tokens.
2684         * For more control over the split use the StrTokenizer class.</p>
2685         *
2686         * <p>A {@code null} input String returns {@code null}.
2687         * A {@code null} separator splits on whitespace.</p>
2688         *
2689         * <pre>
2690         * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *)               = null
2691         * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *)                 = []
2692         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null)      = ["ab", "de", "fg"]
2693         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null)    = ["ab", "", "", "de", "fg"]
2694         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
2695         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
2696         * </pre>
2697         *
2698         * @param str  the String to parse, may be null
2699         * @param separator  String containing the String to be used as a delimiter,
2700         *  {@code null} splits on whitespace
2701         * @return an array of parsed Strings, {@code null} if null String was input
2702         * @since 2.4
2703         */
2704        public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator) {
2705            return splitByWholeSeparatorWorker(str, separator, -1, true);
2706        }
2707    
2708        /**
2709         * <p>Splits the provided text into an array, separator string specified.
2710         * Returns a maximum of {@code max} substrings.</p>
2711         *
2712         * <p>The separator is not included in the returned String array.
2713         * Adjacent separators are treated as separators for empty tokens.
2714         * For more control over the split use the StrTokenizer class.</p>
2715         *
2716         * <p>A {@code null} input String returns {@code null}.
2717         * A {@code null} separator splits on whitespace.</p>
2718         *
2719         * <pre>
2720         * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *)               = null
2721         * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *)                 = []
2722         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0)      = ["ab", "de", "fg"]
2723         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 0)    = ["ab", "", "", "de", "fg"]
2724         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
2725         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
2726         * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
2727         * </pre>
2728         *
2729         * @param str  the String to parse, may be null
2730         * @param separator  String containing the String to be used as a delimiter,
2731         *  {@code null} splits on whitespace
2732         * @param max  the maximum number of elements to include in the returned
2733         *  array. A zero or negative value implies no limit.
2734         * @return an array of parsed Strings, {@code null} if null String was input
2735         * @since 2.4
2736         */
2737        public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator, int max) {
2738            return splitByWholeSeparatorWorker(str, separator, max, true);
2739        }
2740    
2741        /**
2742         * Performs the logic for the {@code splitByWholeSeparatorPreserveAllTokens} methods.
2743         *
2744         * @param str  the String to parse, may be {@code null}
2745         * @param separator  String containing the String to be used as a delimiter,
2746         *  {@code null} splits on whitespace
2747         * @param max  the maximum number of elements to include in the returned
2748         *  array. A zero or negative value implies no limit.
2749         * @param preserveAllTokens if {@code true}, adjacent separators are
2750         * treated as empty token separators; if {@code false}, adjacent
2751         * separators are treated as one separator.
2752         * @return an array of parsed Strings, {@code null} if null String input
2753         * @since 2.4
2754         */
2755        private static String[] splitByWholeSeparatorWorker(
2756                String str, String separator, int max, boolean preserveAllTokens) {
2757            if (str == null) {
2758                return null;
2759            }
2760    
2761            int len = str.length();
2762    
2763            if (len == 0) {
2764                return ArrayUtils.EMPTY_STRING_ARRAY;
2765            }
2766    
2767            if ((separator == null) || (EMPTY.equals(separator))) {
2768                // Split on whitespace.
2769                return splitWorker(str, null, max, preserveAllTokens);
2770            }
2771    
2772            int separatorLength = separator.length();
2773    
2774            ArrayList<String> substrings = new ArrayList<String>();
2775            int numberOfSubstrings = 0;
2776            int beg = 0;
2777            int end = 0;
2778            while (end < len) {
2779                end = str.indexOf(separator, beg);
2780    
2781                if (end > -1) {
2782                    if (end > beg) {
2783                        numberOfSubstrings += 1;
2784    
2785                        if (numberOfSubstrings == max) {
2786                            end = len;
2787                            substrings.add(str.substring(beg));
2788                        } else {
2789                            // The following is OK, because String.substring( beg, end ) excludes
2790                            // the character at the position 'end'.
2791                            substrings.add(str.substring(beg, end));
2792    
2793                            // Set the starting point for the next search.
2794                            // The following is equivalent to beg = end + (separatorLength - 1) + 1,
2795                            // which is the right calculation:
2796                            beg = end + separatorLength;
2797                        }
2798                    } else {
2799                        // We found a consecutive occurrence of the separator, so skip it.
2800                        if (preserveAllTokens) {
2801                            numberOfSubstrings += 1;
2802                            if (numberOfSubstrings == max) {
2803                                end = len;
2804                                substrings.add(str.substring(beg));
2805                            } else {
2806                                substrings.add(EMPTY);
2807                            }
2808                        }
2809                        beg = end + separatorLength;
2810                    }
2811                } else {
2812                    // String.substring( beg ) goes from 'beg' to the end of the String.
2813                    substrings.add(str.substring(beg));
2814                    end = len;
2815                }
2816            }
2817    
2818            return substrings.toArray(new String[substrings.size()]);
2819        }
2820    
2821        // -----------------------------------------------------------------------
2822        /**
2823         * <p>Splits the provided text into an array, using whitespace as the
2824         * separator, preserving all tokens, including empty tokens created by
2825         * adjacent separators. This is an alternative to using StringTokenizer.
2826         * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
2827         *
2828         * <p>The separator is not included in the returned String array.
2829         * Adjacent separators are treated as separators for empty tokens.
2830         * For more control over the split use the StrTokenizer class.</p>
2831         *
2832         * <p>A {@code null} input String returns {@code null}.</p>
2833         *
2834         * <pre>
2835         * StringUtils.splitPreserveAllTokens(null)       = null
2836         * StringUtils.splitPreserveAllTokens("")         = []
2837         * StringUtils.splitPreserveAllTokens("abc def")  = ["abc", "def"]
2838         * StringUtils.splitPreserveAllTokens("abc  def") = ["abc", "", "def"]
2839         * StringUtils.splitPreserveAllTokens(" abc ")    = ["", "abc", ""]
2840         * </pre>
2841         *
2842         * @param str  the String to parse, may be {@code null}
2843         * @return an array of parsed Strings, {@code null} if null String input
2844         * @since 2.1
2845         */
2846        public static String[] splitPreserveAllTokens(String str) {
2847            return splitWorker(str, null, -1, true);
2848        }
2849    
2850        /**
2851         * <p>Splits the provided text into an array, separator specified,
2852         * preserving all tokens, including empty tokens created by adjacent
2853         * separators. This is an alternative to using StringTokenizer.</p>
2854         *
2855         * <p>The separator is not included in the returned String array.
2856         * Adjacent separators are treated as separators for empty tokens.
2857         * For more control over the split use the StrTokenizer class.</p>
2858         *
2859         * <p>A {@code null} input String returns {@code null}.</p>
2860         *
2861         * <pre>
2862         * StringUtils.splitPreserveAllTokens(null, *)         = null
2863         * StringUtils.splitPreserveAllTokens("", *)           = []
2864         * StringUtils.splitPreserveAllTokens("a.b.c", '.')    = ["a", "b", "c"]
2865         * StringUtils.splitPreserveAllTokens("a..b.c", '.')   = ["a", "", "b", "c"]
2866         * StringUtils.splitPreserveAllTokens("a:b:c", '.')    = ["a:b:c"]
2867         * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
2868         * StringUtils.splitPreserveAllTokens("a b c", ' ')    = ["a", "b", "c"]
2869         * StringUtils.splitPreserveAllTokens("a b c ", ' ')   = ["a", "b", "c", ""]
2870         * StringUtils.splitPreserveAllTokens("a b c  ", ' ')   = ["a", "b", "c", "", ""]
2871         * StringUtils.splitPreserveAllTokens(" a b c", ' ')   = ["", a", "b", "c"]
2872         * StringUtils.splitPreserveAllTokens("  a b c", ' ')  = ["", "", a", "b", "c"]
2873         * StringUtils.splitPreserveAllTokens(" a b c ", ' ')  = ["", a", "b", "c", ""]
2874         * </pre>
2875         *
2876         * @param str  the String to parse, may be {@code null}
2877         * @param separatorChar  the character used as the delimiter,
2878         *  {@code null} splits on whitespace
2879         * @return an array of parsed Strings, {@code null} if null String input
2880         * @since 2.1
2881         */
2882        public static String[] splitPreserveAllTokens(String str, char separatorChar) {
2883            return splitWorker(str, separatorChar, true);
2884        }
2885    
2886        /**
2887         * Performs the logic for the {@code split} and
2888         * {@code splitPreserveAllTokens} methods that do not return a
2889         * maximum array length.
2890         *
2891         * @param str  the String to parse, may be {@code null}
2892         * @param separatorChar the separate character
2893         * @param preserveAllTokens if {@code true}, adjacent separators are
2894         * treated as empty token separators; if {@code false}, adjacent
2895         * separators are treated as one separator.
2896         * @return an array of parsed Strings, {@code null} if null String input
2897         */
2898        private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) {
2899            // Performance tuned for 2.0 (JDK1.4)
2900    
2901            if (str == null) {
2902                return null;
2903            }
2904            int len = str.length();
2905            if (len == 0) {
2906                return ArrayUtils.EMPTY_STRING_ARRAY;
2907            }
2908            List<String> list = new ArrayList<String>();
2909            int i = 0, start = 0;
2910            boolean match = false;
2911            boolean lastMatch = false;
2912            while (i < len) {
2913                if (str.charAt(i) == separatorChar) {
2914                    if (match || preserveAllTokens) {
2915                        list.add(str.substring(start, i));
2916                        match = false;
2917                        lastMatch = true;
2918                    }
2919                    start = ++i;
2920                    continue;
2921                }
2922                lastMatch = false;
2923                match = true;
2924                i++;
2925            }
2926            if (match || (preserveAllTokens && lastMatch)) {
2927                list.add(str.substring(start, i));
2928            }
2929            return list.toArray(new String[list.size()]);
2930        }
2931    
2932        /**
2933         * <p>Splits the provided text into an array, separators specified,
2934         * preserving all tokens, including empty tokens created by adjacent
2935         * separators. This is an alternative to using StringTokenizer.</p>
2936         *
2937         * <p>The separator is not included in the returned String array.
2938         * Adjacent separators are treated as separators for empty tokens.
2939         * For more control over the split use the StrTokenizer class.</p>
2940         *
2941         * <p>A {@code null} input String returns {@code null}.
2942         * A {@code null} separatorChars splits on whitespace.</p>
2943         *
2944         * <pre>
2945         * StringUtils.splitPreserveAllTokens(null, *)           = null
2946         * StringUtils.splitPreserveAllTokens("", *)             = []
2947         * StringUtils.splitPreserveAllTokens("abc def", null)   = ["abc", "def"]
2948         * StringUtils.splitPreserveAllTokens("abc def", " ")    = ["abc", "def"]
2949         * StringUtils.splitPreserveAllTokens("abc  def", " ")   = ["abc", "", def"]
2950         * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":")   = ["ab", "cd", "ef"]
2951         * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":")  = ["ab", "cd", "ef", ""]
2952         * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
2953         * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":")  = ["ab", "", cd", "ef"]
2954         * StringUtils.splitPreserveAllTokens(":cd:ef", ":")     = ["", cd", "ef"]
2955         * StringUtils.splitPreserveAllTokens("::cd:ef", ":")    = ["", "", cd", "ef"]
2956         * StringUtils.splitPreserveAllTokens(":cd:ef:", ":")    = ["", cd", "ef", ""]
2957         * </pre>
2958         *
2959         * @param str  the String to parse, may be {@code null}
2960         * @param separatorChars  the characters used as the delimiters,
2961         *  {@code null} splits on whitespace
2962         * @return an array of parsed Strings, {@code null} if null String input
2963         * @since 2.1
2964         */
2965        public static String[] splitPreserveAllTokens(String str, String separatorChars) {
2966            return splitWorker(str, separatorChars, -1, true);
2967        }
2968    
2969        /**
2970         * <p>Splits the provided text into an array with a maximum length,
2971         * separators specified, preserving all tokens, including empty tokens
2972         * created by adjacent separators.</p>
2973         *
2974         * <p>The separator is not included in the returned String array.
2975         * Adjacent separators are treated as separators for empty tokens.
2976         * Adjacent separators are treated as one separator.</p>
2977         *
2978         * <p>A {@code null} input String returns {@code null}.
2979         * A {@code null} separatorChars splits on whitespace.</p>
2980         *
2981         * <p>If more than {@code max} delimited substrings are found, the last
2982         * returned string includes all characters after the first {@code max - 1}
2983         * returned strings (including separator characters).</p>
2984         *
2985         * <pre>
2986         * StringUtils.splitPreserveAllTokens(null, *, *)            = null
2987         * StringUtils.splitPreserveAllTokens("", *, *)              = []
2988         * StringUtils.splitPreserveAllTokens("ab de fg", null, 0)   = ["ab", "cd", "ef"]
2989         * StringUtils.splitPreserveAllTokens("ab   de fg", null, 0) = ["ab", "cd", "ef"]
2990         * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
2991         * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
2992         * StringUtils.splitPreserveAllTokens("ab   de fg", null, 2) = ["ab", "  de fg"]
2993         * StringUtils.splitPreserveAllTokens("ab   de fg", null, 3) = ["ab", "", " de fg"]
2994         * StringUtils.splitPreserveAllTokens("ab   de fg", null, 4) = ["ab", "", "", "de fg"]
2995         * </pre>
2996         *
2997         * @param str  the String to parse, may be {@code null}
2998         * @param separatorChars  the characters used as the delimiters,
2999         *  {@code null} splits on whitespace
3000         * @param max  the maximum number of elements to include in the
3001         *  array. A zero or negative value implies no limit
3002         * @return an array of parsed Strings, {@code null} if null String input
3003         * @since 2.1
3004         */
3005        public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) {
3006            return splitWorker(str, separatorChars, max, true);
3007        }
3008    
3009        /**
3010         * Performs the logic for the {@code split} and
3011         * {@code splitPreserveAllTokens} methods that return a maximum array
3012         * length.
3013         *
3014         * @param str  the String to parse, may be {@code null}
3015         * @param separatorChars the separate character
3016         * @param max  the maximum number of elements to include in the
3017         *  array. A zero or negative value implies no limit.
3018         * @param preserveAllTokens if {@code true}, adjacent separators are
3019         * treated as empty token separators; if {@code false}, adjacent
3020         * separators are treated as one separator.
3021         * @return an array of parsed Strings, {@code null} if null String input
3022         */
3023        private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
3024            // Performance tuned for 2.0 (JDK1.4)
3025            // Direct code is quicker than StringTokenizer.
3026            // Also, StringTokenizer uses isSpace() not isWhitespace()
3027    
3028            if (str == null) {
3029                return null;
3030            }
3031            int len = str.length();
3032            if (len == 0) {
3033                return ArrayUtils.EMPTY_STRING_ARRAY;
3034            }
3035            List<String> list = new ArrayList<String>();
3036            int sizePlus1 = 1;
3037            int i = 0, start = 0;
3038            boolean match = false;
3039            boolean lastMatch = false;
3040            if (separatorChars == null) {
3041                // Null separator means use whitespace
3042                while (i < len) {
3043                    if (Character.isWhitespace(str.charAt(i))) {
3044                        if (match || preserveAllTokens) {
3045                            lastMatch = true;
3046                            if (sizePlus1++ == max) {
3047                                i = len;
3048                                lastMatch = false;
3049                            }
3050                            list.add(str.substring(start, i));
3051                            match = false;
3052                        }
3053                        start = ++i;
3054                        continue;
3055                    }
3056                    lastMatch = false;
3057                    match = true;
3058                    i++;
3059                }
3060            } else if (separatorChars.length() == 1) {
3061                // Optimise 1 character case
3062                char sep = separatorChars.charAt(0);
3063                while (i < len) {
3064                    if (str.charAt(i) == sep) {
3065                        if (match || preserveAllTokens) {
3066                            lastMatch = true;
3067                            if (sizePlus1++ == max) {
3068                                i = len;
3069                                lastMatch = false;
3070                            }
3071                            list.add(str.substring(start, i));
3072                            match = false;
3073                        }
3074                        start = ++i;
3075                        continue;
3076                    }
3077                    lastMatch = false;
3078                    match = true;
3079                    i++;
3080                }
3081            } else {
3082                // standard case
3083                while (i < len) {
3084                    if (separatorChars.indexOf(str.charAt(i)) >= 0) {
3085                        if (match || preserveAllTokens) {
3086                            lastMatch = true;
3087                            if (sizePlus1++ == max) {
3088                                i = len;
3089                                lastMatch = false;
3090                            }
3091                            list.add(str.substring(start, i));
3092                            match = false;
3093                        }
3094                        start = ++i;
3095                        continue;
3096                    }
3097                    lastMatch = false;
3098                    match = true;
3099                    i++;
3100                }
3101            }
3102            if (match || (preserveAllTokens && lastMatch)) {
3103                list.add(str.substring(start, i));
3104            }
3105            return list.toArray(new String[list.size()]);
3106        }
3107    
3108        /**
3109         * <p>Splits a String by Character type as returned by
3110         * {@code java.lang.Character.getType(char)}. Groups of contiguous
3111         * characters of the same type are returned as complete tokens.
3112         * <pre>
3113         * StringUtils.splitByCharacterType(null)         = null
3114         * StringUtils.splitByCharacterType("")           = []
3115         * StringUtils.splitByCharacterType("ab de fg")   = ["ab", " ", "de", " ", "fg"]
3116         * StringUtils.splitByCharacterType("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
3117         * StringUtils.splitByCharacterType("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
3118         * StringUtils.splitByCharacterType("number5")    = ["number", "5"]
3119         * StringUtils.splitByCharacterType("fooBar")     = ["foo", "B", "ar"]
3120         * StringUtils.splitByCharacterType("foo200Bar")  = ["foo", "200", "B", "ar"]
3121         * StringUtils.splitByCharacterType("ASFRules")   = ["ASFR", "ules"]
3122         * </pre>
3123         * @param str the String to split, may be {@code null}
3124         * @return an array of parsed Strings, {@code null} if null String input
3125         * @since 2.4
3126         */
3127        public static String[] splitByCharacterType(String str) {
3128            return splitByCharacterType(str, false);
3129        }
3130    
3131        /**
3132         * <p>Splits a String by Character type as returned by
3133         * {@code java.lang.Character.getType(char)}. Groups of contiguous
3134         * characters of the same type are returned as complete tokens, with the
3135         * following exception: the character of type
3136         * {@code Character.UPPERCASE_LETTER}, if any, immediately
3137         * preceding a token of type {@code Character.LOWERCASE_LETTER}
3138         * will belong to the following token rather than to the preceding, if any,
3139         * {@code Character.UPPERCASE_LETTER} token.
3140         * <pre>
3141         * StringUtils.splitByCharacterTypeCamelCase(null)         = null
3142         * StringUtils.splitByCharacterTypeCamelCase("")           = []
3143         * StringUtils.splitByCharacterTypeCamelCase("ab de fg")   = ["ab", " ", "de", " ", "fg"]
3144         * StringUtils.splitByCharacterTypeCamelCase("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
3145         * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
3146         * StringUtils.splitByCharacterTypeCamelCase("number5")    = ["number", "5"]
3147         * StringUtils.splitByCharacterTypeCamelCase("fooBar")     = ["foo", "Bar"]
3148         * StringUtils.splitByCharacterTypeCamelCase("foo200Bar")  = ["foo", "200", "Bar"]
3149         * StringUtils.splitByCharacterTypeCamelCase("ASFRules")   = ["ASF", "Rules"]
3150         * </pre>
3151         * @param str the String to split, may be {@code null}
3152         * @return an array of parsed Strings, {@code null} if null String input
3153         * @since 2.4
3154         */
3155        public static String[] splitByCharacterTypeCamelCase(String str) {
3156            return splitByCharacterType(str, true);
3157        }
3158    
3159        /**
3160         * <p>Splits a String by Character type as returned by
3161         * {@code java.lang.Character.getType(char)}. Groups of contiguous
3162         * characters of the same type are returned as complete tokens, with the
3163         * following exception: if {@code camelCase} is {@code true},
3164         * the character of type {@code Character.UPPERCASE_LETTER}, if any,
3165         * immediately preceding a token of type {@code Character.LOWERCASE_LETTER}
3166         * will belong to the following token rather than to the preceding, if any,
3167         * {@code Character.UPPERCASE_LETTER} token.
3168         * @param str the String to split, may be {@code null}
3169         * @param camelCase whether to use so-called "camel-case" for letter types
3170         * @return an array of parsed Strings, {@code null} if null String input
3171         * @since 2.4
3172         */
3173        private static String[] splitByCharacterType(String str, boolean camelCase) {
3174            if (str == null) {
3175                return null;
3176            }
3177            if (str.length() == 0) {
3178                return ArrayUtils.EMPTY_STRING_ARRAY;
3179            }
3180            char[] c = str.toCharArray();
3181            List<String> list = new ArrayList<String>();
3182            int tokenStart = 0;
3183            int currentType = Character.getType(c[tokenStart]);
3184            for (int pos = tokenStart + 1; pos < c.length; pos++) {
3185                int type = Character.getType(c[pos]);
3186                if (type == currentType) {
3187                    continue;
3188                }
3189                if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) {
3190                    int newTokenStart = pos - 1;
3191                    if (newTokenStart != tokenStart) {
3192                        list.add(new String(c, tokenStart, newTokenStart - tokenStart));
3193                        tokenStart = newTokenStart;
3194                    }
3195                } else {
3196                    list.add(new String(c, tokenStart, pos - tokenStart));
3197                    tokenStart = pos;
3198                }
3199                currentType = type;
3200            }
3201            list.add(new String(c, tokenStart, c.length - tokenStart));
3202            return list.toArray(new String[list.size()]);
3203        }
3204    
3205        // Joining
3206        //-----------------------------------------------------------------------
3207        /**
3208         * <p>Joins the elements of the provided array into a single String
3209         * containing the provided list of elements.</p>
3210         *
3211         * <p>No separator is added to the joined String.
3212         * Null objects or empty strings within the array are represented by
3213         * empty strings.</p>
3214         *
3215         * <pre>
3216         * StringUtils.join(null)            = null
3217         * StringUtils.join([])              = ""
3218         * StringUtils.join([null])          = ""
3219         * StringUtils.join(["a", "b", "c"]) = "abc"
3220         * StringUtils.join([null, "", "a"]) = "a"
3221         * </pre>
3222         *
3223         * @param <T> the specific type of values to join together
3224         * @param elements  the values to join together, may be null
3225         * @return the joined String, {@code null} if null array input
3226         * @since 2.0
3227         * @since 3.0 Changed signature to use varargs
3228         */
3229        public static <T> String join(T... elements) {
3230            return join(elements, null);
3231        }
3232    
3233        /**
3234         * <p>Joins the elements of the provided array into a single String
3235         * containing the provided list of elements.</p>
3236         *
3237         * <p>No delimiter is added before or after the list.
3238         * Null objects or empty strings within the array are represented by
3239         * empty strings.</p>
3240         *
3241         * <pre>
3242         * StringUtils.join(null, *)               = null
3243         * StringUtils.join([], *)                 = ""
3244         * StringUtils.join([null], *)             = ""
3245         * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
3246         * StringUtils.join(["a", "b", "c"], null) = "abc"
3247         * StringUtils.join([null, "", "a"], ';')  = ";;a"
3248         * </pre>
3249         *
3250         * @param array  the array of values to join together, may be null
3251         * @param separator  the separator character to use
3252         * @return the joined String, {@code null} if null array input
3253         * @since 2.0
3254         */
3255        public static String join(Object[] array, char separator) {
3256            if (array == null) {
3257                return null;
3258            }
3259    
3260            return join(array, separator, 0, array.length);
3261        }
3262    
3263        /**
3264         * <p>Joins the elements of the provided array into a single String
3265         * containing the provided list of elements.</p>
3266         *
3267         * <p>No delimiter is added before or after the list.
3268         * Null objects or empty strings within the array are represented by
3269         * empty strings.</p>
3270         *
3271         * <pre>
3272         * StringUtils.join(null, *)               = null
3273         * StringUtils.join([], *)                 = ""
3274         * StringUtils.join([null], *)             = ""
3275         * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
3276         * StringUtils.join(["a", "b", "c"], null) = "abc"
3277         * StringUtils.join([null, "", "a"], ';')  = ";;a"
3278         * </pre>
3279         *
3280         * @param array  the array of values to join together, may be null
3281         * @param separator  the separator character to use
3282         * @param startIndex the first index to start joining from.  It is
3283         * an error to pass in an end index past the end of the array
3284         * @param endIndex the index to stop joining from (exclusive). It is
3285         * an error to pass in an end index past the end of the array
3286         * @return the joined String, {@code null} if null array input
3287         * @since 2.0
3288         */
3289        public static String join(Object[] array, char separator, int startIndex, int endIndex) {
3290            if (array == null) {
3291                return null;
3292            }
3293            int noOfItems = (endIndex - startIndex);
3294            if (noOfItems <= 0) {
3295                return EMPTY;
3296            }
3297            
3298            StringBuilder buf = new StringBuilder(noOfItems * 16);
3299    
3300            for (int i = startIndex; i < endIndex; i++) {
3301                if (i > startIndex) {
3302                    buf.append(separator);
3303                }
3304                if (array[i] != null) {
3305                    buf.append(array[i]);
3306                }
3307            }
3308            return buf.toString();
3309        }
3310    
3311        /**
3312         * <p>Joins the elements of the provided array into a single String
3313         * containing the provided list of elements.</p>
3314         *
3315         * <p>No delimiter is added before or after the list.
3316         * A {@code null} separator is the same as an empty String ("").
3317         * Null objects or empty strings within the array are represented by
3318         * empty strings.</p>
3319         *
3320         * <pre>
3321         * StringUtils.join(null, *)                = null
3322         * StringUtils.join([], *)                  = ""
3323         * StringUtils.join([null], *)              = ""
3324         * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
3325         * StringUtils.join(["a", "b", "c"], null)  = "abc"
3326         * StringUtils.join(["a", "b", "c"], "")    = "abc"
3327         * StringUtils.join([null, "", "a"], ',')   = ",,a"
3328         * </pre>
3329         *
3330         * @param array  the array of values to join together, may be null
3331         * @param separator  the separator character to use, null treated as ""
3332         * @return the joined String, {@code null} if null array input
3333         */
3334        public static String join(Object[] array, String separator) {
3335            if (array == null) {
3336                return null;
3337            }
3338            return join(array, separator, 0, array.length);
3339        }
3340    
3341        /**
3342         * <p>Joins the elements of the provided array into a single String
3343         * containing the provided list of elements.</p>
3344         *
3345         * <p>No delimiter is added before or after the list.
3346         * A {@code null} separator is the same as an empty String ("").
3347         * Null objects or empty strings within the array are represented by
3348         * empty strings.</p>
3349         *
3350         * <pre>
3351         * StringUtils.join(null, *)                = null
3352         * StringUtils.join([], *)                  = ""
3353         * StringUtils.join([null], *)              = ""
3354         * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
3355         * StringUtils.join(["a", "b", "c"], null)  = "abc"
3356         * StringUtils.join(["a", "b", "c"], "")    = "abc"
3357         * StringUtils.join([null, "", "a"], ',')   = ",,a"
3358         * </pre>
3359         *
3360         * @param array  the array of values to join together, may be null
3361         * @param separator  the separator character to use, null treated as ""
3362         * @param startIndex the first index to start joining from.  It is
3363         * an error to pass in an end index past the end of the array
3364         * @param endIndex the index to stop joining from (exclusive). It is
3365         * an error to pass in an end index past the end of the array
3366         * @return the joined String, {@code null} if null array input
3367         */
3368        public static String join(Object[] array, String separator, int startIndex, int endIndex) {
3369            if (array == null) {
3370                return null;
3371            }
3372            if (separator == null) {
3373                separator = EMPTY;
3374            }
3375    
3376            // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
3377            //           (Assuming that all Strings are roughly equally long)
3378            int noOfItems = (endIndex - startIndex);
3379            if (noOfItems <= 0) {
3380                return EMPTY;
3381            }
3382    
3383            StringBuilder buf = new StringBuilder(noOfItems * 16);
3384    
3385            for (int i = startIndex; i < endIndex; i++) {
3386                if (i > startIndex) {
3387                    buf.append(separator);
3388                }
3389                if (array[i] != null) {
3390                    buf.append(array[i]);
3391                }
3392            }
3393            return buf.toString();
3394        }
3395    
3396        /**
3397         * <p>Joins the elements of the provided {@code Iterator} into
3398         * a single String containing the provided elements.</p>
3399         *
3400         * <p>No delimiter is added before or after the list. Null objects or empty
3401         * strings within the iteration are represented by empty strings.</p>
3402         *
3403         * <p>See the examples here: {@link #join(Object[],char)}. </p>
3404         *
3405         * @param iterator  the {@code Iterator} of values to join together, may be null
3406         * @param separator  the separator character to use
3407         * @return the joined String, {@code null} if null iterator input
3408         * @since 2.0
3409         */
3410        public static String join(Iterator<?> iterator, char separator) {
3411    
3412            // handle null, zero and one elements before building a buffer
3413            if (iterator == null) {
3414                return null;
3415            }
3416            if (!iterator.hasNext()) {
3417                return EMPTY;
3418            }
3419            Object first = iterator.next();
3420            if (!iterator.hasNext()) {
3421                return ObjectUtils.toString(first);
3422            }
3423    
3424            // two or more elements
3425            StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
3426            if (first != null) {
3427                buf.append(first);
3428            }
3429    
3430            while (iterator.hasNext()) {
3431                buf.append(separator);
3432                Object obj = iterator.next();
3433                if (obj != null) {
3434                    buf.append(obj);
3435                }
3436            }
3437    
3438            return buf.toString();
3439        }
3440    
3441        /**
3442         * <p>Joins the elements of the provided {@code Iterator} into
3443         * a single String containing the provided elements.</p>
3444         *
3445         * <p>No delimiter is added before or after the list.
3446         * A {@code null} separator is the same as an empty String ("").</p>
3447         *
3448         * <p>See the examples here: {@link #join(Object[],String)}. </p>
3449         *
3450         * @param iterator  the {@code Iterator} of values to join together, may be null
3451         * @param separator  the separator character to use, null treated as ""
3452         * @return the joined String, {@code null} if null iterator input
3453         */
3454        public static String join(Iterator<?> iterator, String separator) {
3455    
3456            // handle null, zero and one elements before building a buffer
3457            if (iterator == null) {
3458                return null;
3459            }
3460            if (!iterator.hasNext()) {
3461                return EMPTY;
3462            }
3463            Object first = iterator.next();
3464            if (!iterator.hasNext()) {
3465                return ObjectUtils.toString(first);
3466            }
3467    
3468            // two or more elements
3469            StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
3470            if (first != null) {
3471                buf.append(first);
3472            }
3473    
3474            while (iterator.hasNext()) {
3475                if (separator != null) {
3476                    buf.append(separator);
3477                }
3478                Object obj = iterator.next();
3479                if (obj != null) {
3480                    buf.append(obj);
3481                }
3482            }
3483            return buf.toString();
3484        }
3485    
3486        /**
3487         * <p>Joins the elements of the provided {@code Iterable} into
3488         * a single String containing the provided elements.</p>
3489         *
3490         * <p>No delimiter is added before or after the list. Null objects or empty
3491         * strings within the iteration are represented by empty strings.</p>
3492         *
3493         * <p>See the examples here: {@link #join(Object[],char)}. </p>
3494         *
3495         * @param iterable  the {@code Iterable} providing the values to join together, may be null
3496         * @param separator  the separator character to use
3497         * @return the joined String, {@code null} if null iterator input
3498         * @since 2.3
3499         */
3500        public static String join(Iterable<?> iterable, char separator) {
3501            if (iterable == null) {
3502                return null;
3503            }
3504            return join(iterable.iterator(), separator);
3505        }
3506    
3507        /**
3508         * <p>Joins the elements of the provided {@code Iterable} into
3509         * a single String containing the provided elements.</p>
3510         *
3511         * <p>No delimiter is added before or after the list.
3512         * A {@code null} separator is the same as an empty String ("").</p>
3513         *
3514         * <p>See the examples here: {@link #join(Object[],String)}. </p>
3515         *
3516         * @param iterable  the {@code Iterable} providing the values to join together, may be null
3517         * @param separator  the separator character to use, null treated as ""
3518         * @return the joined String, {@code null} if null iterator input
3519         * @since 2.3
3520         */
3521        public static String join(Iterable<?> iterable, String separator) {
3522            if (iterable == null) {
3523                return null;
3524            }
3525            return join(iterable.iterator(), separator);
3526        }
3527    
3528        // Delete
3529        //-----------------------------------------------------------------------
3530        /**
3531         * <p>Deletes all whitespaces from a String as defined by
3532         * {@link Character#isWhitespace(char)}.</p>
3533         *
3534         * <pre>
3535         * StringUtils.deleteWhitespace(null)         = null
3536         * StringUtils.deleteWhitespace("")           = ""
3537         * StringUtils.deleteWhitespace("abc")        = "abc"
3538         * StringUtils.deleteWhitespace("   ab  c  ") = "abc"
3539         * </pre>
3540         *
3541         * @param str  the String to delete whitespace from, may be null
3542         * @return the String without whitespaces, {@code null} if null String input
3543         */
3544        public static String deleteWhitespace(String str) {
3545            if (isEmpty(str)) {
3546                return str;
3547            }
3548            int sz = str.length();
3549            char[] chs = new char[sz];
3550            int count = 0;
3551            for (int i = 0; i < sz; i++) {
3552                if (!Character.isWhitespace(str.charAt(i))) {
3553                    chs[count++] = str.charAt(i);
3554                }
3555            }
3556            if (count == sz) {
3557                return str;
3558            }
3559            return new String(chs, 0, count);
3560        }
3561    
3562        // Remove
3563        //-----------------------------------------------------------------------
3564        /**
3565         * <p>Removes a substring only if it is at the beginning of a source string,
3566         * otherwise returns the source string.</p>
3567         *
3568         * <p>A {@code null} source string will return {@code null}.
3569         * An empty ("") source string will return the empty string.
3570         * A {@code null} search string will return the source string.</p>
3571         *
3572         * <pre>
3573         * StringUtils.removeStart(null, *)      = null
3574         * StringUtils.removeStart("", *)        = ""
3575         * StringUtils.removeStart(*, null)      = *
3576         * StringUtils.removeStart("www.domain.com", "www.")   = "domain.com"
3577         * StringUtils.removeStart("domain.com", "www.")       = "domain.com"
3578         * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com"
3579         * StringUtils.removeStart("abc", "")    = "abc"
3580         * </pre>
3581         *
3582         * @param str  the source String to search, may be null
3583         * @param remove  the String to search for and remove, may be null
3584         * @return the substring with the string removed if found,
3585         *  {@code null} if null String input
3586         * @since 2.1
3587         */
3588        public static String removeStart(String str, String remove) {
3589            if (isEmpty(str) || isEmpty(remove)) {
3590                return str;
3591            }
3592            if (str.startsWith(remove)){
3593                return str.substring(remove.length());
3594            }
3595            return str;
3596        }
3597    
3598        /**
3599         * <p>Case insensitive removal of a substring if it is at the beginning of a source string,
3600         * otherwise returns the source string.</p>
3601         *
3602         * <p>A {@code null} source string will return {@code null}.
3603         * An empty ("") source string will return the empty string.
3604         * A {@code null} search string will return the source string.</p>
3605         *
3606         * <pre>
3607         * StringUtils.removeStartIgnoreCase(null, *)      = null
3608         * StringUtils.removeStartIgnoreCase("", *)        = ""
3609         * StringUtils.removeStartIgnoreCase(*, null)      = *
3610         * StringUtils.removeStartIgnoreCase("www.domain.com", "www.")   = "domain.com"
3611         * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.")   = "domain.com"
3612         * StringUtils.removeStartIgnoreCase("domain.com", "www.")       = "domain.com"
3613         * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
3614         * StringUtils.removeStartIgnoreCase("abc", "")    = "abc"
3615         * </pre>
3616         *
3617         * @param str  the source String to search, may be null
3618         * @param remove  the String to search for (case insensitive) and remove, may be null
3619         * @return the substring with the string removed if found,
3620         *  {@code null} if null String input
3621         * @since 2.4
3622         */
3623        public static String removeStartIgnoreCase(String str, String remove) {
3624            if (isEmpty(str) || isEmpty(remove)) {
3625                return str;
3626            }
3627            if (startsWithIgnoreCase(str, remove)) {
3628                return str.substring(remove.length());
3629            }
3630            return str;
3631        }
3632    
3633        /**
3634         * <p>Removes a substring only if it is at the end of a source string,
3635         * otherwise returns the source string.</p>
3636         *
3637         * <p>A {@code null} source string will return {@code null}.
3638         * An empty ("") source string will return the empty string.
3639         * A {@code null} search string will return the source string.</p>
3640         *
3641         * <pre>
3642         * StringUtils.removeEnd(null, *)      = null
3643         * StringUtils.removeEnd("", *)        = ""
3644         * StringUtils.removeEnd(*, null)      = *
3645         * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
3646         * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
3647         * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
3648         * StringUtils.removeEnd("abc", "")    = "abc"
3649         * </pre>
3650         *
3651         * @param str  the source String to search, may be null
3652         * @param remove  the String to search for and remove, may be null
3653         * @return the substring with the string removed if found,
3654         *  {@code null} if null String input
3655         * @since 2.1
3656         */
3657        public static String removeEnd(String str, String remove) {
3658            if (isEmpty(str) || isEmpty(remove)) {
3659                return str;
3660            }
3661            if (str.endsWith(remove)) {
3662                return str.substring(0, str.length() - remove.length());
3663            }
3664            return str;
3665        }
3666    
3667        /**
3668         * <p>Case insensitive removal of a substring if it is at the end of a source string,
3669         * otherwise returns the source string.</p>
3670         *
3671         * <p>A {@code null} source string will return {@code null}.
3672         * An empty ("") source string will return the empty string.
3673         * A {@code null} search string will return the source string.</p>
3674         *
3675         * <pre>
3676         * StringUtils.removeEndIgnoreCase(null, *)      = null
3677         * StringUtils.removeEndIgnoreCase("", *)        = ""
3678         * StringUtils.removeEndIgnoreCase(*, null)      = *
3679         * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.")  = "www.domain.com"
3680         * StringUtils.removeEndIgnoreCase("www.domain.com", ".com")   = "www.domain"
3681         * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
3682         * StringUtils.removeEndIgnoreCase("abc", "")    = "abc"
3683         * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
3684         * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
3685         * </pre>
3686         *
3687         * @param str  the source String to search, may be null
3688         * @param remove  the String to search for (case insensitive) and remove, may be null
3689         * @return the substring with the string removed if found,
3690         *  {@code null} if null String input
3691         * @since 2.4
3692         */
3693        public static String removeEndIgnoreCase(String str, String remove) {
3694            if (isEmpty(str) || isEmpty(remove)) {
3695                return str;
3696            }
3697            if (endsWithIgnoreCase(str, remove)) {
3698                return str.substring(0, str.length() - remove.length());
3699            }
3700            return str;
3701        }
3702    
3703        /**
3704         * <p>Removes all occurrences of a substring from within the source string.</p>
3705         *
3706         * <p>A {@code null} source string will return {@code null}.
3707         * An empty ("") source string will return the empty string.
3708         * A {@code null} remove string will return the source string.
3709         * An empty ("") remove string will return the source string.</p>
3710         *
3711         * <pre>
3712         * StringUtils.remove(null, *)        = null
3713         * StringUtils.remove("", *)          = ""
3714         * StringUtils.remove(*, null)        = *
3715         * StringUtils.remove(*, "")          = *
3716         * StringUtils.remove("queued", "ue") = "qd"
3717         * StringUtils.remove("queued", "zz") = "queued"
3718         * </pre>
3719         *
3720         * @param str  the source String to search, may be null
3721         * @param remove  the String to search for and remove, may be null
3722         * @return the substring with the string removed if found,
3723         *  {@code null} if null String input
3724         * @since 2.1
3725         */
3726        public static String remove(String str, String remove) {
3727            if (isEmpty(str) || isEmpty(remove)) {
3728                return str;
3729            }
3730            return replace(str, remove, EMPTY, -1);
3731        }
3732    
3733        /**
3734         * <p>Removes all occurrences of a character from within the source string.</p>
3735         *
3736         * <p>A {@code null} source string will return {@code null}.
3737         * An empty ("") source string will return the empty string.</p>
3738         *
3739         * <pre>
3740         * StringUtils.remove(null, *)       = null
3741         * StringUtils.remove("", *)         = ""
3742         * StringUtils.remove("queued", 'u') = "qeed"
3743         * StringUtils.remove("queued", 'z') = "queued"
3744         * </pre>
3745         *
3746         * @param str  the source String to search, may be null
3747         * @param remove  the char to search for and remove, may be null
3748         * @return the substring with the char removed if found,
3749         *  {@code null} if null String input
3750         * @since 2.1
3751         */
3752        public static String remove(String str, char remove) {
3753            if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) {
3754                return str;
3755            }
3756            char[] chars = str.toCharArray();
3757            int pos = 0;
3758            for (int i = 0; i < chars.length; i++) {
3759                if (chars[i] != remove) {
3760                    chars[pos++] = chars[i];
3761                }
3762            }
3763            return new String(chars, 0, pos);
3764        }
3765    
3766        // Replacing
3767        //-----------------------------------------------------------------------
3768        /**
3769         * <p>Replaces a String with another String inside a larger String, once.</p>
3770         *
3771         * <p>A {@code null} reference passed to this method is a no-op.</p>
3772         *
3773         * <pre>
3774         * StringUtils.replaceOnce(null, *, *)        = null
3775         * StringUtils.replaceOnce("", *, *)          = ""
3776         * StringUtils.replaceOnce("any", null, *)    = "any"
3777         * StringUtils.replaceOnce("any", *, null)    = "any"
3778         * StringUtils.replaceOnce("any", "", *)      = "any"
3779         * StringUtils.replaceOnce("aba", "a", null)  = "aba"
3780         * StringUtils.replaceOnce("aba", "a", "")    = "ba"
3781         * StringUtils.replaceOnce("aba", "a", "z")   = "zba"
3782         * </pre>
3783         *
3784         * @see #replace(String text, String searchString, String replacement, int max)
3785         * @param text  text to search and replace in, may be null
3786         * @param searchString  the String to search for, may be null
3787         * @param replacement  the String to replace with, may be null
3788         * @return the text with any replacements processed,
3789         *  {@code null} if null String input
3790         */
3791        public static String replaceOnce(String text, String searchString, String replacement) {
3792            return replace(text, searchString, replacement, 1);
3793        }
3794    
3795        /**
3796         * <p>Replaces all occurrences of a String within another String.</p>
3797         *
3798         * <p>A {@code null} reference passed to this method is a no-op.</p>
3799         *
3800         * <pre>
3801         * StringUtils.replace(null, *, *)        = null
3802         * StringUtils.replace("", *, *)          = ""
3803         * StringUtils.replace("any", null, *)    = "any"
3804         * StringUtils.replace("any", *, null)    = "any"
3805         * StringUtils.replace("any", "", *)      = "any"
3806         * StringUtils.replace("aba", "a", null)  = "aba"
3807         * StringUtils.replace("aba", "a", "")    = "b"
3808         * StringUtils.replace("aba", "a", "z")   = "zbz"
3809         * </pre>
3810         *
3811         * @see #replace(String text, String searchString, String replacement, int max)
3812         * @param text  text to search and replace in, may be null
3813         * @param searchString  the String to search for, may be null
3814         * @param replacement  the String to replace it with, may be null
3815         * @return the text with any replacements processed,
3816         *  {@code null} if null String input
3817         */
3818        public static String replace(String text, String searchString, String replacement) {
3819            return replace(text, searchString, replacement, -1);
3820        }
3821    
3822        /**
3823         * <p>Replaces a String with another String inside a larger String,
3824         * for the first {@code max} values of the search String.</p>
3825         *
3826         * <p>A {@code null} reference passed to this method is a no-op.</p>
3827         *
3828         * <pre>
3829         * StringUtils.replace(null, *, *, *)         = null
3830         * StringUtils.replace("", *, *, *)           = ""
3831         * StringUtils.replace("any", null, *, *)     = "any"
3832         * StringUtils.replace("any", *, null, *)     = "any"
3833         * StringUtils.replace("any", "", *, *)       = "any"
3834         * StringUtils.replace("any", *, *, 0)        = "any"
3835         * StringUtils.replace("abaa", "a", null, -1) = "abaa"
3836         * StringUtils.replace("abaa", "a", "", -1)   = "b"
3837         * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
3838         * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
3839         * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
3840         * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
3841         * </pre>
3842         *
3843         * @param text  text to search and replace in, may be null
3844         * @param searchString  the String to search for, may be null
3845         * @param replacement  the String to replace it with, may be null
3846         * @param max  maximum number of values to replace, or {@code -1} if no maximum
3847         * @return the text with any replacements processed,
3848         *  {@code null} if null String input
3849         */
3850        public static String replace(String text, String searchString, String replacement, int max) {
3851            if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) {
3852                return text;
3853            }
3854            int start = 0;
3855            int end = text.indexOf(searchString, start);
3856            if (end == INDEX_NOT_FOUND) {
3857                return text;
3858            }
3859            int replLength = searchString.length();
3860            int increase = replacement.length() - replLength;
3861            increase = (increase < 0 ? 0 : increase);
3862            increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
3863            StringBuilder buf = new StringBuilder(text.length() + increase);
3864            while (end != INDEX_NOT_FOUND) {
3865                buf.append(text.substring(start, end)).append(replacement);
3866                start = end + replLength;
3867                if (--max == 0) {
3868                    break;
3869                }
3870                end = text.indexOf(searchString, start);
3871            }
3872            buf.append(text.substring(start));
3873            return buf.toString();
3874        }
3875    
3876        /**
3877         * <p>
3878         * Replaces all occurrences of Strings within another String.
3879         * </p>
3880         *
3881         * <p>
3882         * A {@code null} reference passed to this method is a no-op, or if
3883         * any "search string" or "string to replace" is null, that replace will be
3884         * ignored. This will not repeat. For repeating replaces, call the
3885         * overloaded method.
3886         * </p>
3887         *
3888         * <pre>
3889         *  StringUtils.replaceEach(null, *, *)        = null
3890         *  StringUtils.replaceEach("", *, *)          = ""
3891         *  StringUtils.replaceEach("aba", null, null) = "aba"
3892         *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
3893         *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
3894         *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
3895         *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
3896         *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
3897         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
3898         *  (example of how it does not repeat)
3899         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
3900         * </pre>
3901         *
3902         * @param text
3903         *            text to search and replace in, no-op if null
3904         * @param searchList
3905         *            the Strings to search for, no-op if null
3906         * @param replacementList
3907         *            the Strings to replace them with, no-op if null
3908         * @return the text with any replacements processed, {@code null} if
3909         *         null String input
3910         * @throws IndexOutOfBoundsException
3911         *             if the lengths of the arrays are not the same (null is ok,
3912         *             and/or size 0)
3913         * @since 2.4
3914         */
3915        public static String replaceEach(String text, String[] searchList, String[] replacementList) {
3916            return replaceEach(text, searchList, replacementList, false, 0);
3917        }
3918    
3919        /**
3920         * <p>
3921         * Replaces all occurrences of Strings within another String.
3922         * </p>
3923         *
3924         * <p>
3925         * A {@code null} reference passed to this method is a no-op, or if
3926         * any "search string" or "string to replace" is null, that replace will be
3927         * ignored. This will not repeat. For repeating replaces, call the
3928         * overloaded method.
3929         * </p>
3930         *
3931         * <pre>
3932         *  StringUtils.replaceEach(null, *, *, *) = null
3933         *  StringUtils.replaceEach("", *, *, *) = ""
3934         *  StringUtils.replaceEach("aba", null, null, *) = "aba"
3935         *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
3936         *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
3937         *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
3938         *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
3939         *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
3940         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
3941         *  (example of how it repeats)
3942         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
3943         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
3944         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalArgumentException
3945         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
3946         * </pre>
3947         *
3948         * @param text
3949         *            text to search and replace in, no-op if null
3950         * @param searchList
3951         *            the Strings to search for, no-op if null
3952         * @param replacementList
3953         *            the Strings to replace them with, no-op if null
3954         * @return the text with any replacements processed, {@code null} if
3955         *         null String input
3956         * @throws IllegalArgumentException
3957         *             if the search is repeating and there is an endless loop due
3958         *             to outputs of one being inputs to another
3959         * @throws IndexOutOfBoundsException
3960         *             if the lengths of the arrays are not the same (null is ok,
3961         *             and/or size 0)
3962         * @since 2.4
3963         */
3964        public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) {
3965            // timeToLive should be 0 if not used or nothing to replace, else it's
3966            // the length of the replace array
3967            int timeToLive = searchList == null ? 0 : searchList.length;
3968            return replaceEach(text, searchList, replacementList, true, timeToLive);
3969        }
3970    
3971        /**
3972         * <p>
3973         * Replaces all occurrences of Strings within another String.
3974         * </p>
3975         *
3976         * <p>
3977         * A {@code null} reference passed to this method is a no-op, or if
3978         * any "search string" or "string to replace" is null, that replace will be
3979         * ignored.
3980         * </p>
3981         *
3982         * <pre>
3983         *  StringUtils.replaceEach(null, *, *, *) = null
3984         *  StringUtils.replaceEach("", *, *, *) = ""
3985         *  StringUtils.replaceEach("aba", null, null, *) = "aba"
3986         *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
3987         *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
3988         *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
3989         *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
3990         *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
3991         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
3992         *  (example of how it repeats)
3993         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
3994         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
3995         *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalArgumentException
3996         * </pre>
3997         *
3998         * @param text
3999         *            text to search and replace in, no-op if null
4000         * @param searchList
4001         *            the Strings to search for, no-op if null
4002         * @param replacementList
4003         *            the Strings to replace them with, no-op if null
4004         * @param repeat if true, then replace repeatedly
4005         *       until there are no more possible replacements or timeToLive < 0
4006         * @param timeToLive
4007         *            if less than 0 then there is a circular reference and endless
4008         *            loop
4009         * @return the text with any replacements processed, {@code null} if
4010         *         null String input
4011         * @throws IllegalArgumentException
4012         *             if the search is repeating and there is an endless loop due
4013         *             to outputs of one being inputs to another
4014         * @throws IndexOutOfBoundsException
4015         *             if the lengths of the arrays are not the same (null is ok,
4016         *             and/or size 0)
4017         * @since 2.4
4018         */
4019        private static String replaceEach(
4020                String text, String[] searchList, String[] replacementList, boolean repeat, int timeToLive) {
4021    
4022            // mchyzer Performance note: This creates very few new objects (one major goal)
4023            // let me know if there are performance requests, we can create a harness to measure
4024    
4025            if (text == null || text.length() == 0 || searchList == null ||
4026                    searchList.length == 0 || replacementList == null || replacementList.length == 0) {
4027                return text;
4028            }
4029    
4030            // if recursing, this shouldn't be less than 0
4031            if (timeToLive < 0) {
4032                throw new IllegalStateException("TimeToLive of " + timeToLive + " is less than 0: " + text);
4033            }
4034    
4035            int searchLength = searchList.length;
4036            int replacementLength = replacementList.length;
4037    
4038            // make sure lengths are ok, these need to be equal
4039            if (searchLength != replacementLength) {
4040                throw new IllegalArgumentException("Search and Replace array lengths don't match: "
4041                    + searchLength
4042                    + " vs "
4043                    + replacementLength);
4044            }
4045    
4046            // keep track of which still have matches
4047            boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
4048    
4049            // index on index that the match was found
4050            int textIndex = -1;
4051            int replaceIndex = -1;
4052            int tempIndex = -1;
4053    
4054            // index of replace array that will replace the search string found
4055            // NOTE: logic duplicated below START
4056            for (int i = 0; i < searchLength; i++) {
4057                if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
4058                        searchList[i].length() == 0 || replacementList[i] == null) {
4059                    continue;
4060                }
4061                tempIndex = text.indexOf(searchList[i]);
4062    
4063                // see if we need to keep searching for this
4064                if (tempIndex == -1) {
4065                    noMoreMatchesForReplIndex[i] = true;
4066                } else {
4067                    if (textIndex == -1 || tempIndex < textIndex) {
4068                        textIndex = tempIndex;
4069                        replaceIndex = i;
4070                    }
4071                }
4072            }
4073            // NOTE: logic mostly below END
4074    
4075            // no search strings found, we are done
4076            if (textIndex == -1) {
4077                return text;
4078            }
4079    
4080            int start = 0;
4081    
4082            // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
4083            int increase = 0;
4084    
4085            // count the replacement text elements that are larger than their corresponding text being replaced
4086            for (int i = 0; i < searchList.length; i++) {
4087                if (searchList[i] == null || replacementList[i] == null) {
4088                    continue;
4089                }
4090                int greater = replacementList[i].length() - searchList[i].length();
4091                if (greater > 0) {
4092                    increase += 3 * greater; // assume 3 matches
4093                }
4094            }
4095            // have upper-bound at 20% increase, then let Java take over
4096            increase = Math.min(increase, text.length() / 5);
4097    
4098            StringBuilder buf = new StringBuilder(text.length() + increase);
4099    
4100            while (textIndex != -1) {
4101    
4102                for (int i = start; i < textIndex; i++) {
4103                    buf.append(text.charAt(i));
4104                }
4105                buf.append(replacementList[replaceIndex]);
4106    
4107                start = textIndex + searchList[replaceIndex].length();
4108    
4109                textIndex = -1;
4110                replaceIndex = -1;
4111                tempIndex = -1;
4112                // find the next earliest match
4113                // NOTE: logic mostly duplicated above START
4114                for (int i = 0; i < searchLength; i++) {
4115                    if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
4116                            searchList[i].length() == 0 || replacementList[i] == null) {
4117                        continue;
4118                    }
4119                    tempIndex = text.indexOf(searchList[i], start);
4120    
4121                    // see if we need to keep searching for this
4122                    if (tempIndex == -1) {
4123                        noMoreMatchesForReplIndex[i] = true;
4124                    } else {
4125                        if (textIndex == -1 || tempIndex < textIndex) {
4126                            textIndex = tempIndex;
4127                            replaceIndex = i;
4128                        }
4129                    }
4130                }
4131                // NOTE: logic duplicated above END
4132    
4133            }
4134            int textLength = text.length();
4135            for (int i = start; i < textLength; i++) {
4136                buf.append(text.charAt(i));
4137            }
4138            String result = buf.toString();
4139            if (!repeat) {
4140                return result;
4141            }
4142    
4143            return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
4144        }
4145    
4146        // Replace, character based
4147        //-----------------------------------------------------------------------
4148        /**
4149         * <p>Replaces all occurrences of a character in a String with another.
4150         * This is a null-safe version of {@link String#replace(char, char)}.</p>
4151         *
4152         * <p>A {@code null} string input returns {@code null}.
4153         * An empty ("") string input returns an empty string.</p>
4154         *
4155         * <pre>
4156         * StringUtils.replaceChars(null, *, *)        = null
4157         * StringUtils.replaceChars("", *, *)          = ""
4158         * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya"
4159         * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba"
4160         * </pre>
4161         *
4162         * @param str  String to replace characters in, may be null
4163         * @param searchChar  the character to search for, may be null
4164         * @param replaceChar  the character to replace, may be null
4165         * @return modified String, {@code null} if null string input
4166         * @since 2.0
4167         */
4168        public static String replaceChars(String str, char searchChar, char replaceChar) {
4169            if (str == null) {
4170                return null;
4171            }
4172            return str.replace(searchChar, replaceChar);
4173        }
4174    
4175        /**
4176         * <p>Replaces multiple characters in a String in one go.
4177         * This method can also be used to delete characters.</p>
4178         *
4179         * <p>For example:<br />
4180         * <code>replaceChars(&quot;hello&quot;, &quot;ho&quot;, &quot;jy&quot;) = jelly</code>.</p>
4181         *
4182         * <p>A {@code null} string input returns {@code null}.
4183         * An empty ("") string input returns an empty string.
4184         * A null or empty set of search characters returns the input string.</p>
4185         *
4186         * <p>The length of the search characters should normally equal the length
4187         * of the replace characters.
4188         * If the search characters is longer, then the extra search characters
4189         * are deleted.
4190         * If the search characters is shorter, then the extra replace characters
4191         * are ignored.</p>
4192         *
4193         * <pre>
4194         * StringUtils.replaceChars(null, *, *)           = null
4195         * StringUtils.replaceChars("", *, *)             = ""
4196         * StringUtils.replaceChars("abc", null, *)       = "abc"
4197         * StringUtils.replaceChars("abc", "", *)         = "abc"
4198         * StringUtils.replaceChars("abc", "b", null)     = "ac"
4199         * StringUtils.replaceChars("abc", "b", "")       = "ac"
4200         * StringUtils.replaceChars("abcba", "bc", "yz")  = "ayzya"
4201         * StringUtils.replaceChars("abcba", "bc", "y")   = "ayya"
4202         * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya"
4203         * </pre>
4204         *
4205         * @param str  String to replace characters in, may be null
4206         * @param searchChars  a set of characters to search for, may be null
4207         * @param replaceChars  a set of characters to replace, may be null
4208         * @return modified String, {@code null} if null string input
4209         * @since 2.0
4210         */
4211        public static String replaceChars(String str, String searchChars, String replaceChars) {
4212            if (isEmpty(str) || isEmpty(searchChars)) {
4213                return str;
4214            }
4215            if (replaceChars == null) {
4216                replaceChars = EMPTY;
4217            }
4218            boolean modified = false;
4219            int replaceCharsLength = replaceChars.length();
4220            int strLength = str.length();
4221            StringBuilder buf = new StringBuilder(strLength);
4222            for (int i = 0; i < strLength; i++) {
4223                char ch = str.charAt(i);
4224                int index = searchChars.indexOf(ch);
4225                if (index >= 0) {
4226                    modified = true;
4227                    if (index < replaceCharsLength) {
4228                        buf.append(replaceChars.charAt(index));
4229                    }
4230                } else {
4231                    buf.append(ch);
4232                }
4233            }
4234            if (modified) {
4235                return buf.toString();
4236            }
4237            return str;
4238        }
4239    
4240        // Overlay
4241        //-----------------------------------------------------------------------
4242        /**
4243         * <p>Overlays part of a String with another String.</p>
4244         *
4245         * <p>A {@code null} string input returns {@code null}.
4246         * A negative index is treated as zero.
4247         * An index greater than the string length is treated as the string length.
4248         * The start index is always the smaller of the two indices.</p>
4249         *
4250         * <pre>
4251         * StringUtils.overlay(null, *, *, *)            = null
4252         * StringUtils.overlay("", "abc", 0, 0)          = "abc"
4253         * StringUtils.overlay("abcdef", null, 2, 4)     = "abef"
4254         * StringUtils.overlay("abcdef", "", 2, 4)       = "abef"
4255         * StringUtils.overlay("abcdef", "", 4, 2)       = "abef"
4256         * StringUtils.overlay("abcdef", "zzzz", 2, 4)   = "abzzzzef"
4257         * StringUtils.overlay("abcdef", "zzzz", 4, 2)   = "abzzzzef"
4258         * StringUtils.overlay("abcdef", "zzzz", -1, 4)  = "zzzzef"
4259         * StringUtils.overlay("abcdef", "zzzz", 2, 8)   = "abzzzz"
4260         * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef"
4261         * StringUtils.overlay("abcdef", "zzzz", 8, 10)  = "abcdefzzzz"
4262         * </pre>
4263         *
4264         * @param str  the String to do overlaying in, may be null
4265         * @param overlay  the String to overlay, may be null
4266         * @param start  the position to start overlaying at
4267         * @param end  the position to stop overlaying before
4268         * @return overlayed String, {@code null} if null String input
4269         * @since 2.0
4270         */
4271        public static String overlay(String str, String overlay, int start, int end) {
4272            if (str == null) {
4273                return null;
4274            }
4275            if (overlay == null) {
4276                overlay = EMPTY;
4277            }
4278            int len = str.length();
4279            if (start < 0) {
4280                start = 0;
4281            }
4282            if (start > len) {
4283                start = len;
4284            }
4285            if (end < 0) {
4286                end = 0;
4287            }
4288            if (end > len) {
4289                end = len;
4290            }
4291            if (start > end) {
4292                int temp = start;
4293                start = end;
4294                end = temp;
4295            }
4296            return new StringBuilder(len + start - end + overlay.length() + 1)
4297                .append(str.substring(0, start))
4298                .append(overlay)
4299                .append(str.substring(end))
4300                .toString();
4301        }
4302    
4303        // Chomping
4304        //-----------------------------------------------------------------------
4305        /**
4306         * <p>Removes one newline from end of a String if it's there,
4307         * otherwise leave it alone.  A newline is &quot;{@code \n}&quot;,
4308         * &quot;{@code \r}&quot;, or &quot;{@code \r\n}&quot;.</p>
4309         *
4310         * <p>NOTE: This method changed in 2.0.
4311         * It now more closely matches Perl chomp.</p>
4312         *
4313         * <pre>
4314         * StringUtils.chomp(null)          = null
4315         * StringUtils.chomp("")            = ""
4316         * StringUtils.chomp("abc \r")      = "abc "
4317         * StringUtils.chomp("abc\n")       = "abc"
4318         * StringUtils.chomp("abc\r\n")     = "abc"
4319         * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n"
4320         * StringUtils.chomp("abc\n\r")     = "abc\n"
4321         * StringUtils.chomp("abc\n\rabc")  = "abc\n\rabc"
4322         * StringUtils.chomp("\r")          = ""
4323         * StringUtils.chomp("\n")          = ""
4324         * StringUtils.chomp("\r\n")        = ""
4325         * </pre>
4326         *
4327         * @param str  the String to chomp a newline from, may be null
4328         * @return String without newline, {@code null} if null String input
4329         */
4330        public static String chomp(String str) {
4331            if (isEmpty(str)) {
4332                return str;
4333            }
4334    
4335            if (str.length() == 1) {
4336                char ch = str.charAt(0);
4337                if (ch == CharUtils.CR || ch == CharUtils.LF) {
4338                    return EMPTY;
4339                }
4340                return str;
4341            }
4342    
4343            int lastIdx = str.length() - 1;
4344            char last = str.charAt(lastIdx);
4345    
4346            if (last == CharUtils.LF) {
4347                if (str.charAt(lastIdx - 1) == CharUtils.CR) {
4348                    lastIdx--;
4349                }
4350            } else if (last != CharUtils.CR) {
4351                lastIdx++;
4352            }
4353            return str.substring(0, lastIdx);
4354        }
4355    
4356        /**
4357         * <p>Removes {@code separator} from the end of
4358         * {@code str} if it's there, otherwise leave it alone.</p>
4359         *
4360         * <p>NOTE: This method changed in version 2.0.
4361         * It now more closely matches Perl chomp.
4362         * For the previous behavior, use {@link #substringBeforeLast(String, String)}.
4363         * This method uses {@link String#endsWith(String)}.</p>
4364         *
4365         * <pre>
4366         * StringUtils.chomp(null, *)         = null
4367         * StringUtils.chomp("", *)           = ""
4368         * StringUtils.chomp("foobar", "bar") = "foo"
4369         * StringUtils.chomp("foobar", "baz") = "foobar"
4370         * StringUtils.chomp("foo", "foo")    = ""
4371         * StringUtils.chomp("foo ", "foo")   = "foo "
4372         * StringUtils.chomp(" foo", "foo")   = " "
4373         * StringUtils.chomp("foo", "foooo")  = "foo"
4374         * StringUtils.chomp("foo", "")       = "foo"
4375         * StringUtils.chomp("foo", null)     = "foo"
4376         * </pre>
4377         *
4378         * @param str  the String to chomp from, may be null
4379         * @param separator  separator String, may be null
4380         * @return String without trailing separator, {@code null} if null String input
4381         */
4382        public static String chomp(String str, String separator) {
4383            if (isEmpty(str) || separator == null) {
4384                return str;
4385            }
4386            if (str.endsWith(separator)) {
4387                return str.substring(0, str.length() - separator.length());
4388            }
4389            return str;
4390        }
4391    
4392        // Chopping
4393        //-----------------------------------------------------------------------
4394        /**
4395         * <p>Remove the last character from a String.</p>
4396         *
4397         * <p>If the String ends in {@code \r\n}, then remove both
4398         * of them.</p>
4399         *
4400         * <pre>
4401         * StringUtils.chop(null)          = null
4402         * StringUtils.chop("")            = ""
4403         * StringUtils.chop("abc \r")      = "abc "
4404         * StringUtils.chop("abc\n")       = "abc"
4405         * StringUtils.chop("abc\r\n")     = "abc"
4406         * StringUtils.chop("abc")         = "ab"
4407         * StringUtils.chop("abc\nabc")    = "abc\nab"
4408         * StringUtils.chop("a")           = ""
4409         * StringUtils.chop("\r")          = ""
4410         * StringUtils.chop("\n")          = ""
4411         * StringUtils.chop("\r\n")        = ""
4412         * </pre>
4413         *
4414         * @param str  the String to chop last character from, may be null
4415         * @return String without last character, {@code null} if null String input
4416         */
4417        public static String chop(String str) {
4418            if (str == null) {
4419                return null;
4420            }
4421            int strLen = str.length();
4422            if (strLen < 2) {
4423                return EMPTY;
4424            }
4425            int lastIdx = strLen - 1;
4426            String ret = str.substring(0, lastIdx);
4427            char last = str.charAt(lastIdx);
4428            if (last == CharUtils.LF) {
4429                if (ret.charAt(lastIdx - 1) == CharUtils.CR) {
4430                    return ret.substring(0, lastIdx - 1);
4431                }
4432            }
4433            return ret;
4434        }
4435    
4436        // Conversion
4437        //-----------------------------------------------------------------------
4438    
4439        // Padding
4440        //-----------------------------------------------------------------------
4441        /**
4442         * <p>Repeat a String {@code repeat} times to form a
4443         * new String.</p>
4444         *
4445         * <pre>
4446         * StringUtils.repeat(null, 2) = null
4447         * StringUtils.repeat("", 0)   = ""
4448         * StringUtils.repeat("", 2)   = ""
4449         * StringUtils.repeat("a", 3)  = "aaa"
4450         * StringUtils.repeat("ab", 2) = "abab"
4451         * StringUtils.repeat("a", -2) = ""
4452         * </pre>
4453         *
4454         * @param str  the String to repeat, may be null
4455         * @param repeat  number of times to repeat str, negative treated as zero
4456         * @return a new String consisting of the original String repeated,
4457         *  {@code null} if null String input
4458         */
4459        public static String repeat(String str, int repeat) {
4460            // Performance tuned for 2.0 (JDK1.4)
4461    
4462            if (str == null) {
4463                return null;
4464            }
4465            if (repeat <= 0) {
4466                return EMPTY;
4467            }
4468            int inputLength = str.length();
4469            if (repeat == 1 || inputLength == 0) {
4470                return str;
4471            }
4472            if (inputLength == 1 && repeat <= PAD_LIMIT) {
4473                return repeat(str.charAt(0), repeat);
4474            }
4475    
4476            int outputLength = inputLength * repeat;
4477            switch (inputLength) {
4478                case 1 :
4479                    return repeat(str.charAt(0), repeat);
4480                case 2 :
4481                    char ch0 = str.charAt(0);
4482                    char ch1 = str.charAt(1);
4483                    char[] output2 = new char[outputLength];
4484                    for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
4485                        output2[i] = ch0;
4486                        output2[i + 1] = ch1;
4487                    }
4488                    return new String(output2);
4489                default :
4490                    StringBuilder buf = new StringBuilder(outputLength);
4491                    for (int i = 0; i < repeat; i++) {
4492                        buf.append(str);
4493                    }
4494                    return buf.toString();
4495            }
4496        }
4497    
4498        /**
4499         * <p>Repeat a String {@code repeat} times to form a
4500         * new String, with a String separator injected each time. </p>
4501         *
4502         * <pre>
4503         * StringUtils.repeat(null, null, 2) = null
4504         * StringUtils.repeat(null, "x", 2)  = null
4505         * StringUtils.repeat("", null, 0)   = ""
4506         * StringUtils.repeat("", "", 2)     = ""
4507         * StringUtils.repeat("", "x", 3)    = "xxx"
4508         * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
4509         * </pre>
4510         *
4511         * @param str        the String to repeat, may be null
4512         * @param separator  the String to inject, may be null
4513         * @param repeat     number of times to repeat str, negative treated as zero
4514         * @return a new String consisting of the original String repeated,
4515         *  {@code null} if null String input
4516         * @since 2.5
4517         */
4518        public static String repeat(String str, String separator, int repeat) {
4519            if(str == null || separator == null) {
4520                return repeat(str, repeat);
4521            } else {
4522                // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it
4523                String result = repeat(str + separator, repeat);
4524                return removeEnd(result, separator);
4525            }
4526        }
4527    
4528        /**
4529         * <p>Returns padding using the specified delimiter repeated
4530         * to a given length.</p>
4531         *
4532         * <pre>
4533         * StringUtils.repeat(0, 'e')  = ""
4534         * StringUtils.repeat(3, 'e')  = "eee"
4535         * StringUtils.repeat(-2, 'e') = ""
4536         * </pre>
4537         *
4538         * <p>Note: this method doesn't not support padding with
4539         * <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
4540         * as they require a pair of {@code char}s to be represented.
4541         * If you are needing to support full I18N of your applications
4542         * consider using {@link #repeat(String, int)} instead.
4543         * </p>
4544         *
4545         * @param ch  character to repeat
4546         * @param repeat  number of times to repeat char, negative treated as zero
4547         * @return String with repeated character
4548         * @see #repeat(String, int)
4549         */
4550        public static String repeat(char ch, int repeat) {
4551            char[] buf = new char[repeat];
4552            for (int i = repeat - 1; i >= 0; i--) {
4553                buf[i] = ch;
4554            }
4555            return new String(buf);
4556        }
4557    
4558        /**
4559         * <p>Right pad a String with spaces (' ').</p>
4560         *
4561         * <p>The String is padded to the size of {@code size}.</p>
4562         *
4563         * <pre>
4564         * StringUtils.rightPad(null, *)   = null
4565         * StringUtils.rightPad("", 3)     = "   "
4566         * StringUtils.rightPad("bat", 3)  = "bat"
4567         * StringUtils.rightPad("bat", 5)  = "bat  "
4568         * StringUtils.rightPad("bat", 1)  = "bat"
4569         * StringUtils.rightPad("bat", -1) = "bat"
4570         * </pre>
4571         *
4572         * @param str  the String to pad out, may be null
4573         * @param size  the size to pad to
4574         * @return right padded String or original String if no padding is necessary,
4575         *  {@code null} if null String input
4576         */
4577        public static String rightPad(String str, int size) {
4578            return rightPad(str, size, ' ');
4579        }
4580    
4581        /**
4582         * <p>Right pad a String with a specified character.</p>
4583         *
4584         * <p>The String is padded to the size of {@code size}.</p>
4585         *
4586         * <pre>
4587         * StringUtils.rightPad(null, *, *)     = null
4588         * StringUtils.rightPad("", 3, 'z')     = "zzz"
4589         * StringUtils.rightPad("bat", 3, 'z')  = "bat"
4590         * StringUtils.rightPad("bat", 5, 'z')  = "batzz"
4591         * StringUtils.rightPad("bat", 1, 'z')  = "bat"
4592         * StringUtils.rightPad("bat", -1, 'z') = "bat"
4593         * </pre>
4594         *
4595         * @param str  the String to pad out, may be null
4596         * @param size  the size to pad to
4597         * @param padChar  the character to pad with
4598         * @return right padded String or original String if no padding is necessary,
4599         *  {@code null} if null String input
4600         * @since 2.0
4601         */
4602        public static String rightPad(String str, int size, char padChar) {
4603            if (str == null) {
4604                return null;
4605            }
4606            int pads = size - str.length();
4607            if (pads <= 0) {
4608                return str; // returns original String when possible
4609            }
4610            if (pads > PAD_LIMIT) {
4611                return rightPad(str, size, String.valueOf(padChar));
4612            }
4613            return str.concat(repeat(padChar, pads));
4614        }
4615    
4616        /**
4617         * <p>Right pad a String with a specified String.</p>
4618         *
4619         * <p>The String is padded to the size of {@code size}.</p>
4620         *
4621         * <pre>
4622         * StringUtils.rightPad(null, *, *)      = null
4623         * StringUtils.rightPad("", 3, "z")      = "zzz"
4624         * StringUtils.rightPad("bat", 3, "yz")  = "bat"
4625         * StringUtils.rightPad("bat", 5, "yz")  = "batyz"
4626         * StringUtils.rightPad("bat", 8, "yz")  = "batyzyzy"
4627         * StringUtils.rightPad("bat", 1, "yz")  = "bat"
4628         * StringUtils.rightPad("bat", -1, "yz") = "bat"
4629         * StringUtils.rightPad("bat", 5, null)  = "bat  "
4630         * StringUtils.rightPad("bat", 5, "")    = "bat  "
4631         * </pre>
4632         *
4633         * @param str  the String to pad out, may be null
4634         * @param size  the size to pad to
4635         * @param padStr  the String to pad with, null or empty treated as single space
4636         * @return right padded String or original String if no padding is necessary,
4637         *  {@code null} if null String input
4638         */
4639        public static String rightPad(String str, int size, String padStr) {
4640            if (str == null) {
4641                return null;
4642            }
4643            if (isEmpty(padStr)) {
4644                padStr = " ";
4645            }
4646            int padLen = padStr.length();
4647            int strLen = str.length();
4648            int pads = size - strLen;
4649            if (pads <= 0) {
4650                return str; // returns original String when possible
4651            }
4652            if (padLen == 1 && pads <= PAD_LIMIT) {
4653                return rightPad(str, size, padStr.charAt(0));
4654            }
4655    
4656            if (pads == padLen) {
4657                return str.concat(padStr);
4658            } else if (pads < padLen) {
4659                return str.concat(padStr.substring(0, pads));
4660            } else {
4661                char[] padding = new char[pads];
4662                char[] padChars = padStr.toCharArray();
4663                for (int i = 0; i < pads; i++) {
4664                    padding[i] = padChars[i % padLen];
4665                }
4666                return str.concat(new String(padding));
4667            }
4668        }
4669    
4670        /**
4671         * <p>Left pad a String with spaces (' ').</p>
4672         *
4673         * <p>The String is padded to the size of {@code size}.</p>
4674         *
4675         * <pre>
4676         * StringUtils.leftPad(null, *)   = null
4677         * StringUtils.leftPad("", 3)     = "   "
4678         * StringUtils.leftPad("bat", 3)  = "bat"
4679         * StringUtils.leftPad("bat", 5)  = "  bat"
4680         * StringUtils.leftPad("bat", 1)  = "bat"
4681         * StringUtils.leftPad("bat", -1) = "bat"
4682         * </pre>
4683         *
4684         * @param str  the String to pad out, may be null
4685         * @param size  the size to pad to
4686         * @return left padded String or original String if no padding is necessary,
4687         *  {@code null} if null String input
4688         */
4689        public static String leftPad(String str, int size) {
4690            return leftPad(str, size, ' ');
4691        }
4692    
4693        /**
4694         * <p>Left pad a String with a specified character.</p>
4695         *
4696         * <p>Pad to a size of {@code size}.</p>
4697         *
4698         * <pre>
4699         * StringUtils.leftPad(null, *, *)     = null
4700         * StringUtils.leftPad("", 3, 'z')     = "zzz"
4701         * StringUtils.leftPad("bat", 3, 'z')  = "bat"
4702         * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
4703         * StringUtils.leftPad("bat", 1, 'z')  = "bat"
4704         * StringUtils.leftPad("bat", -1, 'z') = "bat"
4705         * </pre>
4706         *
4707         * @param str  the String to pad out, may be null
4708         * @param size  the size to pad to
4709         * @param padChar  the character to pad with
4710         * @return left padded String or original String if no padding is necessary,
4711         *  {@code null} if null String input
4712         * @since 2.0
4713         */
4714        public static String leftPad(String str, int size, char padChar) {
4715            if (str == null) {
4716                return null;
4717            }
4718            int pads = size - str.length();
4719            if (pads <= 0) {
4720                return str; // returns original String when possible
4721            }
4722            if (pads > PAD_LIMIT) {
4723                return leftPad(str, size, String.valueOf(padChar));
4724            }
4725            return repeat(padChar, pads).concat(str);
4726        }
4727    
4728        /**
4729         * <p>Left pad a String with a specified String.</p>
4730         *
4731         * <p>Pad to a size of {@code size}.</p>
4732         *
4733         * <pre>
4734         * StringUtils.leftPad(null, *, *)      = null
4735         * StringUtils.leftPad("", 3, "z")      = "zzz"
4736         * StringUtils.leftPad("bat", 3, "yz")  = "bat"
4737         * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
4738         * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
4739         * StringUtils.leftPad("bat", 1, "yz")  = "bat"
4740         * StringUtils.leftPad("bat", -1, "yz") = "bat"
4741         * StringUtils.leftPad("bat", 5, null)  = "  bat"
4742         * StringUtils.leftPad("bat", 5, "")    = "  bat"
4743         * </pre>
4744         *
4745         * @param str  the String to pad out, may be null
4746         * @param size  the size to pad to
4747         * @param padStr  the String to pad with, null or empty treated as single space
4748         * @return left padded String or original String if no padding is necessary,
4749         *  {@code null} if null String input
4750         */
4751        public static String leftPad(String str, int size, String padStr) {
4752            if (str == null) {
4753                return null;
4754            }
4755            if (isEmpty(padStr)) {
4756                padStr = " ";
4757            }
4758            int padLen = padStr.length();
4759            int strLen = str.length();
4760            int pads = size - strLen;
4761            if (pads <= 0) {
4762                return str; // returns original String when possible
4763            }
4764            if (padLen == 1 && pads <= PAD_LIMIT) {
4765                return leftPad(str, size, padStr.charAt(0));
4766            }
4767    
4768            if (pads == padLen) {
4769                return padStr.concat(str);
4770            } else if (pads < padLen) {
4771                return padStr.substring(0, pads).concat(str);
4772            } else {
4773                char[] padding = new char[pads];
4774                char[] padChars = padStr.toCharArray();
4775                for (int i = 0; i < pads; i++) {
4776                    padding[i] = padChars[i % padLen];
4777                }
4778                return new String(padding).concat(str);
4779            }
4780        }
4781    
4782        /**
4783         * Gets a CharSequence length or {@code 0} if the CharSequence is
4784         * {@code null}.
4785         *
4786         * @param cs
4787         *            a CharSequence or {@code null}
4788         * @return CharSequence length or {@code 0} if the CharSequence is
4789         *         {@code null}.
4790         * @since 2.4
4791         * @since 3.0 Changed signature from length(String) to length(CharSequence)
4792         */
4793        public static int length(CharSequence cs) {
4794            return cs == null ? 0 : cs.length();
4795        }
4796    
4797        // Centering
4798        //-----------------------------------------------------------------------
4799        /**
4800         * <p>Centers a String in a larger String of size {@code size}
4801         * using the space character (' ').<p>
4802         *
4803         * <p>If the size is less than the String length, the String is returned.
4804         * A {@code null} String returns {@code null}.
4805         * A negative size is treated as zero.</p>
4806         *
4807         * <p>Equivalent to {@code center(str, size, " ")}.</p>
4808         *
4809         * <pre>
4810         * StringUtils.center(null, *)   = null
4811         * StringUtils.center("", 4)     = "    "
4812         * StringUtils.center("ab", -1)  = "ab"
4813         * StringUtils.center("ab", 4)   = " ab "
4814         * StringUtils.center("abcd", 2) = "abcd"
4815         * StringUtils.center("a", 4)    = " a  "
4816         * </pre>
4817         *
4818         * @param str  the String to center, may be null
4819         * @param size  the int size of new String, negative treated as zero
4820         * @return centered String, {@code null} if null String input
4821         */
4822        public static String center(String str, int size) {
4823            return center(str, size, ' ');
4824        }
4825    
4826        /**
4827         * <p>Centers a String in a larger String of size {@code size}.
4828         * Uses a supplied character as the value to pad the String with.</p>
4829         *
4830         * <p>If the size is less than the String length, the String is returned.
4831         * A {@code null} String returns {@code null}.
4832         * A negative size is treated as zero.</p>
4833         *
4834         * <pre>
4835         * StringUtils.center(null, *, *)     = null
4836         * StringUtils.center("", 4, ' ')     = "    "
4837         * StringUtils.center("ab", -1, ' ')  = "ab"
4838         * StringUtils.center("ab", 4, ' ')   = " ab"
4839         * StringUtils.center("abcd", 2, ' ') = "abcd"
4840         * StringUtils.center("a", 4, ' ')    = " a  "
4841         * StringUtils.center("a", 4, 'y')    = "yayy"
4842         * </pre>
4843         *
4844         * @param str  the String to center, may be null
4845         * @param size  the int size of new String, negative treated as zero
4846         * @param padChar  the character to pad the new String with
4847         * @return centered String, {@code null} if null String input
4848         * @since 2.0
4849         */
4850        public static String center(String str, int size, char padChar) {
4851            if (str == null || size <= 0) {
4852                return str;
4853            }
4854            int strLen = str.length();
4855            int pads = size - strLen;
4856            if (pads <= 0) {
4857                return str;
4858            }
4859            str = leftPad(str, strLen + pads / 2, padChar);
4860            str = rightPad(str, size, padChar);
4861            return str;
4862        }
4863    
4864        /**
4865         * <p>Centers a String in a larger String of size {@code size}.
4866         * Uses a supplied String as the value to pad the String with.</p>
4867         *
4868         * <p>If the size is less than the String length, the String is returned.
4869         * A {@code null} String returns {@code null}.
4870         * A negative size is treated as zero.</p>
4871         *
4872         * <pre>
4873         * StringUtils.center(null, *, *)     = null
4874         * StringUtils.center("", 4, " ")     = "    "
4875         * StringUtils.center("ab", -1, " ")  = "ab"
4876         * StringUtils.center("ab", 4, " ")   = " ab"
4877         * StringUtils.center("abcd", 2, " ") = "abcd"
4878         * StringUtils.center("a", 4, " ")    = " a  "
4879         * StringUtils.center("a", 4, "yz")   = "yayz"
4880         * StringUtils.center("abc", 7, null) = "  abc  "
4881         * StringUtils.center("abc", 7, "")   = "  abc  "
4882         * </pre>
4883         *
4884         * @param str  the String to center, may be null
4885         * @param size  the int size of new String, negative treated as zero
4886         * @param padStr  the String to pad the new String with, must not be null or empty
4887         * @return centered String, {@code null} if null String input
4888         * @throws IllegalArgumentException if padStr is {@code null} or empty
4889         */
4890        public static String center(String str, int size, String padStr) {
4891            if (str == null || size <= 0) {
4892                return str;
4893            }
4894            if (isEmpty(padStr)) {
4895                padStr = " ";
4896            }
4897            int strLen = str.length();
4898            int pads = size - strLen;
4899            if (pads <= 0) {
4900                return str;
4901            }
4902            str = leftPad(str, strLen + pads / 2, padStr);
4903            str = rightPad(str, size, padStr);
4904            return str;
4905        }
4906    
4907        // Case conversion
4908        //-----------------------------------------------------------------------
4909        /**
4910         * <p>Converts a String to upper case as per {@link String#toUpperCase()}.</p>
4911         *
4912         * <p>A {@code null} input String returns {@code null}.</p>
4913         *
4914         * <pre>
4915         * StringUtils.upperCase(null)  = null
4916         * StringUtils.upperCase("")    = ""
4917         * StringUtils.upperCase("aBc") = "ABC"
4918         * </pre>
4919         *
4920         * <p><strong>Note:</strong> As described in the documentation for {@link String#toUpperCase()},
4921         * the result of this method is affected by the current locale.
4922         * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)}
4923         * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p>
4924         *
4925         * @param str  the String to upper case, may be null
4926         * @return the upper cased String, {@code null} if null String input
4927         */
4928        public static String upperCase(String str) {
4929            if (str == null) {
4930                return null;
4931            }
4932            return str.toUpperCase();
4933        }
4934    
4935        /**
4936         * <p>Converts a String to upper case as per {@link String#toUpperCase(Locale)}.</p>
4937         *
4938         * <p>A {@code null} input String returns {@code null}.</p>
4939         *
4940         * <pre>
4941         * StringUtils.upperCase(null, Locale.ENGLISH)  = null
4942         * StringUtils.upperCase("", Locale.ENGLISH)    = ""
4943         * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC"
4944         * </pre>
4945         *
4946         * @param str  the String to upper case, may be null
4947         * @param locale  the locale that defines the case transformation rules, must not be null
4948         * @return the upper cased String, {@code null} if null String input
4949         * @since 2.5
4950         */
4951        public static String upperCase(String str, Locale locale) {
4952            if (str == null) {
4953                return null;
4954            }
4955            return str.toUpperCase(locale);
4956        }
4957    
4958        /**
4959         * <p>Converts a String to lower case as per {@link String#toLowerCase()}.</p>
4960         *
4961         * <p>A {@code null} input String returns {@code null}.</p>
4962         *
4963         * <pre>
4964         * StringUtils.lowerCase(null)  = null
4965         * StringUtils.lowerCase("")    = ""
4966         * StringUtils.lowerCase("aBc") = "abc"
4967         * </pre>
4968         *
4969         * <p><strong>Note:</strong> As described in the documentation for {@link String#toLowerCase()},
4970         * the result of this method is affected by the current locale.
4971         * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)}
4972         * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p>
4973         *
4974         * @param str  the String to lower case, may be null
4975         * @return the lower cased String, {@code null} if null String input
4976         */
4977        public static String lowerCase(String str) {
4978            if (str == null) {
4979                return null;
4980            }
4981            return str.toLowerCase();
4982        }
4983    
4984        /**
4985         * <p>Converts a String to lower case as per {@link String#toLowerCase(Locale)}.</p>
4986         *
4987         * <p>A {@code null} input String returns {@code null}.</p>
4988         *
4989         * <pre>
4990         * StringUtils.lowerCase(null, Locale.ENGLISH)  = null
4991         * StringUtils.lowerCase("", Locale.ENGLISH)    = ""
4992         * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc"
4993         * </pre>
4994         *
4995         * @param str  the String to lower case, may be null
4996         * @param locale  the locale that defines the case transformation rules, must not be null
4997         * @return the lower cased String, {@code null} if null String input
4998         * @since 2.5
4999         */
5000        public static String lowerCase(String str, Locale locale) {
5001            if (str == null) {
5002                return null;
5003            }
5004            return str.toLowerCase(locale);
5005        }
5006    
5007        /**
5008         * <p>Capitalizes a String changing the first letter to title case as
5009         * per {@link Character#toTitleCase(char)}. No other letters are changed.</p>
5010         *
5011         * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}.
5012         * A {@code null} input String returns {@code null}.</p>
5013         *
5014         * <pre>
5015         * StringUtils.capitalize(null)  = null
5016         * StringUtils.capitalize("")    = ""
5017         * StringUtils.capitalize("cat") = "Cat"
5018         * StringUtils.capitalize("cAt") = "CAt"
5019         * </pre>
5020         *
5021         * @param str the String to capitalize, may be null
5022         * @return the capitalized String, {@code null} if null String input
5023         * @see org.apache.commons.lang3.text.WordUtils#capitalize(String)
5024         * @see #uncapitalize(String)
5025         * @since 2.0
5026         */
5027        public static String capitalize(String str) {
5028            int strLen;
5029            if (str == null || (strLen = str.length()) == 0) {
5030                return str;
5031            }
5032            return new StringBuilder(strLen)
5033                .append(Character.toTitleCase(str.charAt(0)))
5034                .append(str.substring(1))
5035                .toString();
5036        }
5037    
5038        /**
5039         * <p>Uncapitalizes a String changing the first letter to title case as
5040         * per {@link Character#toLowerCase(char)}. No other letters are changed.</p>
5041         *
5042         * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}.
5043         * A {@code null} input String returns {@code null}.</p>
5044         *
5045         * <pre>
5046         * StringUtils.uncapitalize(null)  = null
5047         * StringUtils.uncapitalize("")    = ""
5048         * StringUtils.uncapitalize("Cat") = "cat"
5049         * StringUtils.uncapitalize("CAT") = "cAT"
5050         * </pre>
5051         *
5052         * @param str the String to uncapitalize, may be null
5053         * @return the uncapitalized String, {@code null} if null String input
5054         * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String)
5055         * @see #capitalize(String)
5056         * @since 2.0
5057         */
5058        public static String uncapitalize(String str) {
5059            int strLen;
5060            if (str == null || (strLen = str.length()) == 0) {
5061                return str;
5062            }
5063            return new StringBuilder(strLen)
5064                .append(Character.toLowerCase(str.charAt(0)))
5065                .append(str.substring(1))
5066                .toString();
5067        }
5068    
5069        /**
5070         * <p>Swaps the case of a String changing upper and title case to
5071         * lower case, and lower case to upper case.</p>
5072         *
5073         * <ul>
5074         *  <li>Upper case character converts to Lower case</li>
5075         *  <li>Title case character converts to Lower case</li>
5076         *  <li>Lower case character converts to Upper case</li>
5077         * </ul>
5078         *
5079         * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}.
5080         * A {@code null} input String returns {@code null}.</p>
5081         *
5082         * <pre>
5083         * StringUtils.swapCase(null)                 = null
5084         * StringUtils.swapCase("")                   = ""
5085         * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
5086         * </pre>
5087         *
5088         * <p>NOTE: This method changed in Lang version 2.0.
5089         * It no longer performs a word based algorithm.
5090         * If you only use ASCII, you will notice no change.
5091         * That functionality is available in org.apache.commons.lang3.text.WordUtils.</p>
5092         *
5093         * @param str  the String to swap case, may be null
5094         * @return the changed String, {@code null} if null String input
5095         */
5096        public static String swapCase(String str) {
5097            if (StringUtils.isEmpty(str)) {
5098                return str;
5099            }
5100    
5101            char[] buffer = str.toCharArray();
5102    
5103            for (int i = 0; i < buffer.length; i++) {
5104                char ch = buffer[i];
5105                if (Character.isUpperCase(ch)) {
5106                    buffer[i] = Character.toLowerCase(ch);
5107                } else if (Character.isTitleCase(ch)) {
5108                    buffer[i] = Character.toLowerCase(ch);
5109                } else if (Character.isLowerCase(ch)) {
5110                    buffer[i] = Character.toUpperCase(ch);
5111                }
5112            }
5113            return new String(buffer);
5114        }
5115    
5116        // Count matches
5117        //-----------------------------------------------------------------------
5118        /**
5119         * <p>Counts how many times the substring appears in the larger string.</p>
5120         *
5121         * <p>A {@code null} or empty ("") String input returns {@code 0}.</p>
5122         *
5123         * <pre>
5124         * StringUtils.countMatches(null, *)       = 0
5125         * StringUtils.countMatches("", *)         = 0
5126         * StringUtils.countMatches("abba", null)  = 0
5127         * StringUtils.countMatches("abba", "")    = 0
5128         * StringUtils.countMatches("abba", "a")   = 2
5129         * StringUtils.countMatches("abba", "ab")  = 1
5130         * StringUtils.countMatches("abba", "xxx") = 0
5131         * </pre>
5132         *
5133         * @param str  the CharSequence to check, may be null
5134         * @param sub  the substring to count, may be null
5135         * @return the number of occurrences, 0 if either CharSequence is {@code null}
5136         * @since 3.0 Changed signature from countMatches(String, String) to countMatches(CharSequence, CharSequence)
5137         */
5138        public static int countMatches(CharSequence str, CharSequence sub) {
5139            if (isEmpty(str) || isEmpty(sub)) {
5140                return 0;
5141            }
5142            int count = 0;
5143            int idx = 0;
5144            while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) {
5145                count++;
5146                idx += sub.length();
5147            }
5148            return count;
5149        }
5150    
5151        // Character Tests
5152        //-----------------------------------------------------------------------
5153        /**
5154         * <p>Checks if the CharSequence contains only Unicode letters.</p>
5155         *
5156         * <p>{@code null} will return {@code false}.
5157         * An empty CharSequence (length()=0) will return {@code false}.</p>
5158         *
5159         * <pre>
5160         * StringUtils.isAlpha(null)   = false
5161         * StringUtils.isAlpha("")     = false
5162         * StringUtils.isAlpha("  ")   = false
5163         * StringUtils.isAlpha("abc")  = true
5164         * StringUtils.isAlpha("ab2c") = false
5165         * StringUtils.isAlpha("ab-c") = false
5166         * </pre>
5167         *
5168         * @param cs  the CharSequence to check, may be null
5169         * @return {@code true} if only contains letters, and is non-null
5170         * @since 3.0 Changed signature from isAlpha(String) to isAlpha(CharSequence)
5171         * @since 3.0 Changed "" to return false and not true
5172         */
5173        public static boolean isAlpha(CharSequence cs) {
5174            if (cs == null || cs.length() == 0) {
5175                return false;
5176            }
5177            int sz = cs.length();
5178            for (int i = 0; i < sz; i++) {
5179                if (Character.isLetter(cs.charAt(i)) == false) {
5180                    return false;
5181                }
5182            }
5183            return true;
5184        }
5185    
5186        /**
5187         * <p>Checks if the CharSequence contains only Unicode letters and
5188         * space (' ').</p>
5189         *
5190         * <p>{@code null} will return {@code false}
5191         * An empty CharSequence (length()=0) will return {@code true}.</p>
5192         *
5193         * <pre>
5194         * StringUtils.isAlphaSpace(null)   = false
5195         * StringUtils.isAlphaSpace("")     = true
5196         * StringUtils.isAlphaSpace("  ")   = true
5197         * StringUtils.isAlphaSpace("abc")  = true
5198         * StringUtils.isAlphaSpace("ab c") = true
5199         * StringUtils.isAlphaSpace("ab2c") = false
5200         * StringUtils.isAlphaSpace("ab-c") = false
5201         * </pre>
5202         *
5203         * @param cs  the CharSequence to check, may be null
5204         * @return {@code true} if only contains letters and space,
5205         *  and is non-null
5206         * @since 3.0 Changed signature from isAlphaSpace(String) to isAlphaSpace(CharSequence)
5207         */
5208        public static boolean isAlphaSpace(CharSequence cs) {
5209            if (cs == null) {
5210                return false;
5211            }
5212            int sz = cs.length();
5213            for (int i = 0; i < sz; i++) {
5214                if ((Character.isLetter(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
5215                    return false;
5216                }
5217            }
5218            return true;
5219        }
5220    
5221        /**
5222         * <p>Checks if the CharSequence contains only Unicode letters or digits.</p>
5223         *
5224         * <p>{@code null} will return {@code false}.
5225         * An empty CharSequence (length()=0) will return {@code false}.</p>
5226         *
5227         * <pre>
5228         * StringUtils.isAlphanumeric(null)   = false
5229         * StringUtils.isAlphanumeric("")     = false
5230         * StringUtils.isAlphanumeric("  ")   = false
5231         * StringUtils.isAlphanumeric("abc")  = true
5232         * StringUtils.isAlphanumeric("ab c") = false
5233         * StringUtils.isAlphanumeric("ab2c") = true
5234         * StringUtils.isAlphanumeric("ab-c") = false
5235         * </pre>
5236         *
5237         * @param cs  the CharSequence to check, may be null
5238         * @return {@code true} if only contains letters or digits,
5239         *  and is non-null
5240         * @since 3.0 Changed signature from isAlphanumeric(String) to isAlphanumeric(CharSequence)
5241         * @since 3.0 Changed "" to return false and not true
5242         */
5243        public static boolean isAlphanumeric(CharSequence cs) {
5244            if (cs == null || cs.length() == 0) {
5245                return false;
5246            }
5247            int sz = cs.length();
5248            for (int i = 0; i < sz; i++) {
5249                if (Character.isLetterOrDigit(cs.charAt(i)) == false) {
5250                    return false;
5251                }
5252            }
5253            return true;
5254        }
5255    
5256        /**
5257         * <p>Checks if the CharSequence contains only Unicode letters, digits
5258         * or space ({@code ' '}).</p>
5259         *
5260         * <p>{@code null} will return {@code false}.
5261         * An empty CharSequence (length()=0) will return {@code true}.</p>
5262         *
5263         * <pre>
5264         * StringUtils.isAlphanumericSpace(null)   = false
5265         * StringUtils.isAlphanumericSpace("")     = true
5266         * StringUtils.isAlphanumericSpace("  ")   = true
5267         * StringUtils.isAlphanumericSpace("abc")  = true
5268         * StringUtils.isAlphanumericSpace("ab c") = true
5269         * StringUtils.isAlphanumericSpace("ab2c") = true
5270         * StringUtils.isAlphanumericSpace("ab-c") = false
5271         * </pre>
5272         *
5273         * @param cs  the CharSequence to check, may be null
5274         * @return {@code true} if only contains letters, digits or space,
5275         *  and is non-null
5276         * @since 3.0 Changed signature from isAlphanumericSpace(String) to isAlphanumericSpace(CharSequence)
5277         */
5278        public static boolean isAlphanumericSpace(CharSequence cs) {
5279            if (cs == null) {
5280                return false;
5281            }
5282            int sz = cs.length();
5283            for (int i = 0; i < sz; i++) {
5284                if ((Character.isLetterOrDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
5285                    return false;
5286                }
5287            }
5288            return true;
5289        }
5290    
5291        /**
5292         * <p>Checks if the CharSequence contains only ASCII printable characters.</p>
5293         *
5294         * <p>{@code null} will return {@code false}.
5295         * An empty CharSequence (length()=0) will return {@code true}.</p>
5296         *
5297         * <pre>
5298         * StringUtils.isAsciiPrintable(null)     = false
5299         * StringUtils.isAsciiPrintable("")       = true
5300         * StringUtils.isAsciiPrintable(" ")      = true
5301         * StringUtils.isAsciiPrintable("Ceki")   = true
5302         * StringUtils.isAsciiPrintable("ab2c")   = true
5303         * StringUtils.isAsciiPrintable("!ab-c~") = true
5304         * StringUtils.isAsciiPrintable("\u0020") = true
5305         * StringUtils.isAsciiPrintable("\u0021") = true
5306         * StringUtils.isAsciiPrintable("\u007e") = true
5307         * StringUtils.isAsciiPrintable("\u007f") = false
5308         * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
5309         * </pre>
5310         *
5311         * @param cs the CharSequence to check, may be null
5312         * @return {@code true} if every character is in the range
5313         *  32 thru 126
5314         * @since 2.1
5315         * @since 3.0 Changed signature from isAsciiPrintable(String) to isAsciiPrintable(CharSequence)
5316         */
5317        public static boolean isAsciiPrintable(CharSequence cs) {
5318            if (cs == null) {
5319                return false;
5320            }
5321            int sz = cs.length();
5322            for (int i = 0; i < sz; i++) {
5323                if (CharUtils.isAsciiPrintable(cs.charAt(i)) == false) {
5324                    return false;
5325                }
5326            }
5327            return true;
5328        }
5329    
5330        /**
5331         * <p>Checks if the CharSequence contains only Unicode digits.
5332         * A decimal point is not a Unicode digit and returns false.</p>
5333         *
5334         * <p>{@code null} will return {@code false}.
5335         * An empty CharSequence (length()=0) will return {@code false}.</p>
5336         *
5337         * <pre>
5338         * StringUtils.isNumeric(null)   = false
5339         * StringUtils.isNumeric("")     = false
5340         * StringUtils.isNumeric("  ")   = false
5341         * StringUtils.isNumeric("123")  = true
5342         * StringUtils.isNumeric("12 3") = false
5343         * StringUtils.isNumeric("ab2c") = false
5344         * StringUtils.isNumeric("12-3") = false
5345         * StringUtils.isNumeric("12.3") = false
5346         * </pre>
5347         *
5348         * @param cs  the CharSequence to check, may be null
5349         * @return {@code true} if only contains digits, and is non-null
5350         * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence)
5351         * @since 3.0 Changed "" to return false and not true
5352         */
5353        public static boolean isNumeric(CharSequence cs) {
5354            if (cs == null || cs.length() == 0) {
5355                return false;
5356            }
5357            int sz = cs.length();
5358            for (int i = 0; i < sz; i++) {
5359                if (Character.isDigit(cs.charAt(i)) == false) {
5360                    return false;
5361                }
5362            }
5363            return true;
5364        }
5365    
5366        /**
5367         * <p>Checks if the CharSequence contains only Unicode digits or space
5368         * ({@code ' '}).
5369         * A decimal point is not a Unicode digit and returns false.</p>
5370         *
5371         * <p>{@code null} will return {@code false}.
5372         * An empty CharSequence (length()=0) will return {@code true}.</p>
5373         *
5374         * <pre>
5375         * StringUtils.isNumericSpace(null)   = false
5376         * StringUtils.isNumericSpace("")     = true
5377         * StringUtils.isNumericSpace("  ")   = true
5378         * StringUtils.isNumericSpace("123")  = true
5379         * StringUtils.isNumericSpace("12 3") = true
5380         * StringUtils.isNumericSpace("ab2c") = false
5381         * StringUtils.isNumericSpace("12-3") = false
5382         * StringUtils.isNumericSpace("12.3") = false
5383         * </pre>
5384         *
5385         * @param cs  the CharSequence to check, may be null
5386         * @return {@code true} if only contains digits or space,
5387         *  and is non-null
5388         * @since 3.0 Changed signature from isNumericSpace(String) to isNumericSpace(CharSequence)
5389         */
5390        public static boolean isNumericSpace(CharSequence cs) {
5391            if (cs == null) {
5392                return false;
5393            }
5394            int sz = cs.length();
5395            for (int i = 0; i < sz; i++) {
5396                if ((Character.isDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
5397                    return false;
5398                }
5399            }
5400            return true;
5401        }
5402    
5403        /**
5404         * <p>Checks if the CharSequence contains only whitespace.</p>
5405         *
5406         * <p>{@code null} will return {@code false}.
5407         * An empty CharSequence (length()=0) will return {@code true}.</p>
5408         *
5409         * <pre>
5410         * StringUtils.isWhitespace(null)   = false
5411         * StringUtils.isWhitespace("")     = true
5412         * StringUtils.isWhitespace("  ")   = true
5413         * StringUtils.isWhitespace("abc")  = false
5414         * StringUtils.isWhitespace("ab2c") = false
5415         * StringUtils.isWhitespace("ab-c") = false
5416         * </pre>
5417         *
5418         * @param cs  the CharSequence to check, may be null
5419         * @return {@code true} if only contains whitespace, and is non-null
5420         * @since 2.0
5421         * @since 3.0 Changed signature from isWhitespace(String) to isWhitespace(CharSequence)
5422         */
5423        public static boolean isWhitespace(CharSequence cs) {
5424            if (cs == null) {
5425                return false;
5426            }
5427            int sz = cs.length();
5428            for (int i = 0; i < sz; i++) {
5429                if ((Character.isWhitespace(cs.charAt(i)) == false)) {
5430                    return false;
5431                }
5432            }
5433            return true;
5434        }
5435    
5436        /**
5437         * <p>Checks if the CharSequence contains only lowercase characters.</p>
5438         *
5439         * <p>{@code null} will return {@code false}.
5440         * An empty CharSequence (length()=0) will return {@code false}.</p>
5441         *
5442         * <pre>
5443         * StringUtils.isAllLowerCase(null)   = false
5444         * StringUtils.isAllLowerCase("")     = false
5445         * StringUtils.isAllLowerCase("  ")   = false
5446         * StringUtils.isAllLowerCase("abc")  = true
5447         * StringUtils.isAllLowerCase("abC") = false
5448         * </pre>
5449         *
5450         * @param cs  the CharSequence to check, may be null
5451         * @return {@code true} if only contains lowercase characters, and is non-null
5452         * @since 2.5
5453         * @since 3.0 Changed signature from isAllLowerCase(String) to isAllLowerCase(CharSequence)
5454         */
5455        public static boolean isAllLowerCase(CharSequence cs) {
5456            if (cs == null || isEmpty(cs)) {
5457                return false;
5458            }
5459            int sz = cs.length();
5460            for (int i = 0; i < sz; i++) {
5461                if (Character.isLowerCase(cs.charAt(i)) == false) {
5462                    return false;
5463                }
5464            }
5465            return true;
5466        }
5467    
5468        /**
5469         * <p>Checks if the CharSequence contains only uppercase characters.</p>
5470         *
5471         * <p>{@code null} will return {@code false}.
5472         * An empty String (length()=0) will return {@code false}.</p>
5473         *
5474         * <pre>
5475         * StringUtils.isAllUpperCase(null)   = false
5476         * StringUtils.isAllUpperCase("")     = false
5477         * StringUtils.isAllUpperCase("  ")   = false
5478         * StringUtils.isAllUpperCase("ABC")  = true
5479         * StringUtils.isAllUpperCase("aBC") = false
5480         * </pre>
5481         *
5482         * @param cs the CharSequence to check, may be null
5483         * @return {@code true} if only contains uppercase characters, and is non-null
5484         * @since 2.5
5485         * @since 3.0 Changed signature from isAllUpperCase(String) to isAllUpperCase(CharSequence)
5486         */
5487        public static boolean isAllUpperCase(CharSequence cs) {
5488            if (cs == null || isEmpty(cs)) {
5489                return false;
5490            }
5491            int sz = cs.length();
5492            for (int i = 0; i < sz; i++) {
5493                if (Character.isUpperCase(cs.charAt(i)) == false) {
5494                    return false;
5495                }
5496            }
5497            return true;
5498        }
5499    
5500        // Defaults
5501        //-----------------------------------------------------------------------
5502        /**
5503         * <p>Returns either the passed in String,
5504         * or if the String is {@code null}, an empty String ("").</p>
5505         *
5506         * <pre>
5507         * StringUtils.defaultString(null)  = ""
5508         * StringUtils.defaultString("")    = ""
5509         * StringUtils.defaultString("bat") = "bat"
5510         * </pre>
5511         *
5512         * @see ObjectUtils#toString(Object)
5513         * @see String#valueOf(Object)
5514         * @param str  the String to check, may be null
5515         * @return the passed in String, or the empty String if it
5516         *  was {@code null}
5517         */
5518        public static String defaultString(String str) {
5519            return str == null ? EMPTY : str;
5520        }
5521    
5522        /**
5523         * <p>Returns either the passed in String, or if the String is
5524         * {@code null}, the value of {@code defaultStr}.</p>
5525         *
5526         * <pre>
5527         * StringUtils.defaultString(null, "NULL")  = "NULL"
5528         * StringUtils.defaultString("", "NULL")    = ""
5529         * StringUtils.defaultString("bat", "NULL") = "bat"
5530         * </pre>
5531         *
5532         * @see ObjectUtils#toString(Object,String)
5533         * @see String#valueOf(Object)
5534         * @param str  the String to check, may be null
5535         * @param defaultStr  the default String to return
5536         *  if the input is {@code null}, may be null
5537         * @return the passed in String, or the default if it was {@code null}
5538         */
5539        public static String defaultString(String str, String defaultStr) {
5540            return str == null ? defaultStr : str;
5541        }
5542    
5543        /**
5544         * <p>Returns either the passed in CharSequence, or if the CharSequence is
5545         * whitespace, empty ("") or {@code null}, the value of {@code defaultStr}.</p>
5546         *
5547         * <pre>
5548         * StringUtils.defaultIfBlank(null, "NULL")  = "NULL"
5549         * StringUtils.defaultIfBlank("", "NULL")    = "NULL"
5550         * StringUtils.defaultIfBlank(" ", "NULL")   = "NULL"
5551         * StringUtils.defaultIfBlank("bat", "NULL") = "bat"
5552         * StringUtils.defaultIfBlank("", null)      = null
5553         * </pre>
5554         * @param <T> the specific kind of CharSequence
5555         * @param str the CharSequence to check, may be null
5556         * @param defaultStr  the default CharSequence to return
5557         *  if the input is whitespace, empty ("") or {@code null}, may be null
5558         * @return the passed in CharSequence, or the default
5559         * @see StringUtils#defaultString(String, String)
5560         */
5561        public static <T extends CharSequence> T defaultIfBlank(T str, T defaultStr) {
5562            return StringUtils.isBlank(str) ? defaultStr : str;
5563        }
5564    
5565        /**
5566         * <p>Returns either the passed in CharSequence, or if the CharSequence is
5567         * empty or {@code null}, the value of {@code defaultStr}.</p>
5568         *
5569         * <pre>
5570         * StringUtils.defaultIfEmpty(null, "NULL")  = "NULL"
5571         * StringUtils.defaultIfEmpty("", "NULL")    = "NULL"
5572         * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
5573         * StringUtils.defaultIfEmpty("", null)      = null
5574         * </pre>
5575         * @param <T> the specific kind of CharSequence
5576         * @param str  the CharSequence to check, may be null
5577         * @param defaultStr  the default CharSequence to return
5578         *  if the input is empty ("") or {@code null}, may be null
5579         * @return the passed in CharSequence, or the default
5580         * @see StringUtils#defaultString(String, String)
5581         */
5582        public static <T extends CharSequence> T defaultIfEmpty(T str, T defaultStr) {
5583            return StringUtils.isEmpty(str) ? defaultStr : str;
5584        }
5585    
5586        // Reversing
5587        //-----------------------------------------------------------------------
5588        /**
5589         * <p>Reverses a String as per {@link StringBuilder#reverse()}.</p>
5590         *
5591         * <p>A {@code null} String returns {@code null}.</p>
5592         *
5593         * <pre>
5594         * StringUtils.reverse(null)  = null
5595         * StringUtils.reverse("")    = ""
5596         * StringUtils.reverse("bat") = "tab"
5597         * </pre>
5598         *
5599         * @param str  the String to reverse, may be null
5600         * @return the reversed String, {@code null} if null String input
5601         */
5602        public static String reverse(String str) {
5603            if (str == null) {
5604                return null;
5605            }
5606            return new StringBuilder(str).reverse().toString();
5607        }
5608    
5609        /**
5610         * <p>Reverses a String that is delimited by a specific character.</p>
5611         *
5612         * <p>The Strings between the delimiters are not reversed.
5613         * Thus java.lang.String becomes String.lang.java (if the delimiter
5614         * is {@code '.'}).</p>
5615         *
5616         * <pre>
5617         * StringUtils.reverseDelimited(null, *)      = null
5618         * StringUtils.reverseDelimited("", *)        = ""
5619         * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c"
5620         * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a"
5621         * </pre>
5622         *
5623         * @param str  the String to reverse, may be null
5624         * @param separatorChar  the separator character to use
5625         * @return the reversed String, {@code null} if null String input
5626         * @since 2.0
5627         */
5628        public static String reverseDelimited(String str, char separatorChar) {
5629            if (str == null) {
5630                return null;
5631            }
5632            // could implement manually, but simple way is to reuse other,
5633            // probably slower, methods.
5634            String[] strs = split(str, separatorChar);
5635            ArrayUtils.reverse(strs);
5636            return join(strs, separatorChar);
5637        }
5638    
5639        // Abbreviating
5640        //-----------------------------------------------------------------------
5641        /**
5642         * <p>Abbreviates a String using ellipses. This will turn
5643         * "Now is the time for all good men" into "Now is the time for..."</p>
5644         *
5645         * <p>Specifically:
5646         * <ul>
5647         *   <li>If {@code str} is less than {@code maxWidth} characters
5648         *       long, return it.</li>
5649         *   <li>Else abbreviate it to {@code (substring(str, 0, max-3) + "...")}.</li>
5650         *   <li>If {@code maxWidth} is less than {@code 4}, throw an
5651         *       {@code IllegalArgumentException}.</li>
5652         *   <li>In no case will it return a String of length greater than
5653         *       {@code maxWidth}.</li>
5654         * </ul>
5655         * </p>
5656         *
5657         * <pre>
5658         * StringUtils.abbreviate(null, *)      = null
5659         * StringUtils.abbreviate("", 4)        = ""
5660         * StringUtils.abbreviate("abcdefg", 6) = "abc..."
5661         * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
5662         * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
5663         * StringUtils.abbreviate("abcdefg", 4) = "a..."
5664         * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
5665         * </pre>
5666         *
5667         * @param str  the String to check, may be null
5668         * @param maxWidth  maximum length of result String, must be at least 4
5669         * @return abbreviated String, {@code null} if null String input
5670         * @throws IllegalArgumentException if the width is too small
5671         * @since 2.0
5672         */
5673        public static String abbreviate(String str, int maxWidth) {
5674            return abbreviate(str, 0, maxWidth);
5675        }
5676    
5677        /**
5678         * <p>Abbreviates a String using ellipses. This will turn
5679         * "Now is the time for all good men" into "...is the time for..."</p>
5680         *
5681         * <p>Works like {@code abbreviate(String, int)}, but allows you to specify
5682         * a "left edge" offset.  Note that this left edge is not necessarily going to
5683         * be the leftmost character in the result, or the first character following the
5684         * ellipses, but it will appear somewhere in the result.
5685         *
5686         * <p>In no case will it return a String of length greater than
5687         * {@code maxWidth}.</p>
5688         *
5689         * <pre>
5690         * StringUtils.abbreviate(null, *, *)                = null
5691         * StringUtils.abbreviate("", 0, 4)                  = ""
5692         * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
5693         * StringUtils.abbreviate("abcdefghijklmno", 0, 10)  = "abcdefg..."
5694         * StringUtils.abbreviate("abcdefghijklmno", 1, 10)  = "abcdefg..."
5695         * StringUtils.abbreviate("abcdefghijklmno", 4, 10)  = "abcdefg..."
5696         * StringUtils.abbreviate("abcdefghijklmno", 5, 10)  = "...fghi..."
5697         * StringUtils.abbreviate("abcdefghijklmno", 6, 10)  = "...ghij..."
5698         * StringUtils.abbreviate("abcdefghijklmno", 8, 10)  = "...ijklmno"
5699         * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
5700         * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
5701         * StringUtils.abbreviate("abcdefghij", 0, 3)        = IllegalArgumentException
5702         * StringUtils.abbreviate("abcdefghij", 5, 6)        = IllegalArgumentException
5703         * </pre>
5704         *
5705         * @param str  the String to check, may be null
5706         * @param offset  left edge of source String
5707         * @param maxWidth  maximum length of result String, must be at least 4
5708         * @return abbreviated String, {@code null} if null String input
5709         * @throws IllegalArgumentException if the width is too small
5710         * @since 2.0
5711         */
5712        public static String abbreviate(String str, int offset, int maxWidth) {
5713            if (str == null) {
5714                return null;
5715            }
5716            if (maxWidth < 4) {
5717                throw new IllegalArgumentException("Minimum abbreviation width is 4");
5718            }
5719            if (str.length() <= maxWidth) {
5720                return str;
5721            }
5722            if (offset > str.length()) {
5723                offset = str.length();
5724            }
5725            if ((str.length() - offset) < (maxWidth - 3)) {
5726                offset = str.length() - (maxWidth - 3);
5727            }
5728            final String abrevMarker = "...";
5729            if (offset <= 4) {
5730                return str.substring(0, maxWidth - 3) + abrevMarker;
5731            }
5732            if (maxWidth < 7) {
5733                throw new IllegalArgumentException("Minimum abbreviation width with offset is 7");
5734            }
5735            if ((offset + (maxWidth - 3)) < str.length()) {
5736                return abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
5737            }
5738            return abrevMarker + str.substring(str.length() - (maxWidth - 3));
5739        }
5740    
5741        /**
5742         * <p>Abbreviates a String to the length passed, replacing the middle characters with the supplied
5743         * replacement String.</p>
5744         *
5745         * <p>This abbreviation only occurs if the following criteria is met:
5746         * <ul>
5747         * <li>Neither the String for abbreviation nor the replacement String are null or empty </li>
5748         * <li>The length to truncate to is less than the length of the supplied String</li>
5749         * <li>The length to truncate to is greater than 0</li>
5750         * <li>The abbreviated String will have enough room for the length supplied replacement String
5751         * and the first and last characters of the supplied String for abbreviation</li>
5752         * </ul>
5753         * Otherwise, the returned String will be the same as the supplied String for abbreviation.
5754         * </p>
5755         *
5756         * <pre>
5757         * StringUtils.abbreviateMiddle(null, null, 0)      = null
5758         * StringUtils.abbreviateMiddle("abc", null, 0)      = "abc"
5759         * StringUtils.abbreviateMiddle("abc", ".", 0)      = "abc"
5760         * StringUtils.abbreviateMiddle("abc", ".", 3)      = "abc"
5761         * StringUtils.abbreviateMiddle("abcdef", ".", 4)     = "ab.f"
5762         * </pre>
5763         *
5764         * @param str  the String to abbreviate, may be null
5765         * @param middle the String to replace the middle characters with, may be null
5766         * @param length the length to abbreviate {@code str} to.
5767         * @return the abbreviated String if the above criteria is met, or the original String supplied for abbreviation.
5768         * @since 2.5
5769         */
5770        public static String abbreviateMiddle(String str, String middle, int length) {
5771            if (isEmpty(str) || isEmpty(middle)) {
5772                return str;
5773            }
5774    
5775            if (length >= str.length() || length < (middle.length()+2)) {
5776                return str;
5777            }
5778    
5779            int targetSting = length-middle.length();
5780            int startOffset = targetSting/2+targetSting%2;
5781            int endOffset = str.length()-targetSting/2;
5782    
5783            StringBuilder builder = new StringBuilder(length);
5784            builder.append(str.substring(0,startOffset));
5785            builder.append(middle);
5786            builder.append(str.substring(endOffset));
5787    
5788            return builder.toString();
5789        }
5790    
5791        // Difference
5792        //-----------------------------------------------------------------------
5793        /**
5794         * <p>Compares two Strings, and returns the portion where they differ.
5795         * (More precisely, return the remainder of the second String,
5796         * starting from where it's different from the first.)</p>
5797         *
5798         * <p>For example,
5799         * {@code difference("i am a machine", "i am a robot") -> "robot"}.</p>
5800         *
5801         * <pre>
5802         * StringUtils.difference(null, null) = null
5803         * StringUtils.difference("", "") = ""
5804         * StringUtils.difference("", "abc") = "abc"
5805         * StringUtils.difference("abc", "") = ""
5806         * StringUtils.difference("abc", "abc") = ""
5807         * StringUtils.difference("ab", "abxyz") = "xyz"
5808         * StringUtils.difference("abcde", "abxyz") = "xyz"
5809         * StringUtils.difference("abcde", "xyz") = "xyz"
5810         * </pre>
5811         *
5812         * @param str1  the first String, may be null
5813         * @param str2  the second String, may be null
5814         * @return the portion of str2 where it differs from str1; returns the
5815         * empty String if they are equal
5816         * @since 2.0
5817         */
5818        public static String difference(String str1, String str2) {
5819            if (str1 == null) {
5820                return str2;
5821            }
5822            if (str2 == null) {
5823                return str1;
5824            }
5825            int at = indexOfDifference(str1, str2);
5826            if (at == INDEX_NOT_FOUND) {
5827                return EMPTY;
5828            }
5829            return str2.substring(at);
5830        }
5831    
5832        /**
5833         * <p>Compares two CharSequences, and returns the index at which the
5834         * CharSequences begin to differ.</p>
5835         *
5836         * <p>For example,
5837         * {@code indexOfDifference("i am a machine", "i am a robot") -> 7}</p>
5838         *
5839         * <pre>
5840         * StringUtils.indexOfDifference(null, null) = -1
5841         * StringUtils.indexOfDifference("", "") = -1
5842         * StringUtils.indexOfDifference("", "abc") = 0
5843         * StringUtils.indexOfDifference("abc", "") = 0
5844         * StringUtils.indexOfDifference("abc", "abc") = -1
5845         * StringUtils.indexOfDifference("ab", "abxyz") = 2
5846         * StringUtils.indexOfDifference("abcde", "abxyz") = 2
5847         * StringUtils.indexOfDifference("abcde", "xyz") = 0
5848         * </pre>
5849         *
5850         * @param cs1  the first CharSequence, may be null
5851         * @param cs2  the second CharSequence, may be null
5852         * @return the index where cs1 and cs2 begin to differ; -1 if they are equal
5853         * @since 2.0
5854         * @since 3.0 Changed signature from indexOfDifference(String, String) to
5855         * indexOfDifference(CharSequence, CharSequence)
5856         */
5857        public static int indexOfDifference(CharSequence cs1, CharSequence cs2) {
5858            if (cs1 == cs2) {
5859                return INDEX_NOT_FOUND;
5860            }
5861            if (cs1 == null || cs2 == null) {
5862                return 0;
5863            }
5864            int i;
5865            for (i = 0; i < cs1.length() && i < cs2.length(); ++i) {
5866                if (cs1.charAt(i) != cs2.charAt(i)) {
5867                    break;
5868                }
5869            }
5870            if (i < cs2.length() || i < cs1.length()) {
5871                return i;
5872            }
5873            return INDEX_NOT_FOUND;
5874        }
5875    
5876        /**
5877         * <p>Compares all CharSequences in an array and returns the index at which the
5878         * CharSequences begin to differ.</p>
5879         *
5880         * <p>For example,
5881         * <code>indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7</code></p>
5882         *
5883         * <pre>
5884         * StringUtils.indexOfDifference(null) = -1
5885         * StringUtils.indexOfDifference(new String[] {}) = -1
5886         * StringUtils.indexOfDifference(new String[] {"abc"}) = -1
5887         * StringUtils.indexOfDifference(new String[] {null, null}) = -1
5888         * StringUtils.indexOfDifference(new String[] {"", ""}) = -1
5889         * StringUtils.indexOfDifference(new String[] {"", null}) = 0
5890         * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0
5891         * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0
5892         * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0
5893         * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0
5894         * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1
5895         * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1
5896         * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2
5897         * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
5898         * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0
5899         * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0
5900         * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
5901         * </pre>
5902         *
5903         * @param css  array of CharSequences, entries may be null
5904         * @return the index where the strings begin to differ; -1 if they are all equal
5905         * @since 2.4
5906         * @since 3.0 Changed signature from indexOfDifference(String...) to indexOfDifference(CharSequence...)
5907         */
5908        public static int indexOfDifference(CharSequence... css) {
5909            if (css == null || css.length <= 1) {
5910                return INDEX_NOT_FOUND;
5911            }
5912            boolean anyStringNull = false;
5913            boolean allStringsNull = true;
5914            int arrayLen = css.length;
5915            int shortestStrLen = Integer.MAX_VALUE;
5916            int longestStrLen = 0;
5917    
5918            // find the min and max string lengths; this avoids checking to make
5919            // sure we are not exceeding the length of the string each time through
5920            // the bottom loop.
5921            for (int i = 0; i < arrayLen; i++) {
5922                if (css[i] == null) {
5923                    anyStringNull = true;
5924                    shortestStrLen = 0;
5925                } else {
5926                    allStringsNull = false;
5927                    shortestStrLen = Math.min(css[i].length(), shortestStrLen);
5928                    longestStrLen = Math.max(css[i].length(), longestStrLen);
5929                }
5930            }
5931    
5932            // handle lists containing all nulls or all empty strings
5933            if (allStringsNull || (longestStrLen == 0 && !anyStringNull)) {
5934                return INDEX_NOT_FOUND;
5935            }
5936    
5937            // handle lists containing some nulls or some empty strings
5938            if (shortestStrLen == 0) {
5939                return 0;
5940            }
5941    
5942            // find the position with the first difference across all strings
5943            int firstDiff = -1;
5944            for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) {
5945                char comparisonChar = css[0].charAt(stringPos);
5946                for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) {
5947                    if (css[arrayPos].charAt(stringPos) != comparisonChar) {
5948                        firstDiff = stringPos;
5949                        break;
5950                    }
5951                }
5952                if (firstDiff != -1) {
5953                    break;
5954                }
5955            }
5956    
5957            if (firstDiff == -1 && shortestStrLen != longestStrLen) {
5958                // we compared all of the characters up to the length of the
5959                // shortest string and didn't find a match, but the string lengths
5960                // vary, so return the length of the shortest string.
5961                return shortestStrLen;
5962            }
5963            return firstDiff;
5964        }
5965    
5966        /**
5967         * <p>Compares all Strings in an array and returns the initial sequence of
5968         * characters that is common to all of them.</p>
5969         *
5970         * <p>For example,
5971         * <code>getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "</code></p>
5972         *
5973         * <pre>
5974         * StringUtils.getCommonPrefix(null) = ""
5975         * StringUtils.getCommonPrefix(new String[] {}) = ""
5976         * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
5977         * StringUtils.getCommonPrefix(new String[] {null, null}) = ""
5978         * StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
5979         * StringUtils.getCommonPrefix(new String[] {"", null}) = ""
5980         * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
5981         * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
5982         * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
5983         * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
5984         * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
5985         * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
5986         * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
5987         * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
5988         * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
5989         * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
5990         * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
5991         * </pre>
5992         *
5993         * @param strs  array of String objects, entries may be null
5994         * @return the initial sequence of characters that are common to all Strings
5995         * in the array; empty String if the array is null, the elements are all null
5996         * or if there is no common prefix.
5997         * @since 2.4
5998         */
5999        public static String getCommonPrefix(String... strs) {
6000            if (strs == null || strs.length == 0) {
6001                return EMPTY;
6002            }
6003            int smallestIndexOfDiff = indexOfDifference(strs);
6004            if (smallestIndexOfDiff == INDEX_NOT_FOUND) {
6005                // all strings were identical
6006                if (strs[0] == null) {
6007                    return EMPTY;
6008                }
6009                return strs[0];
6010            } else if (smallestIndexOfDiff == 0) {
6011                // there were no common initial characters
6012                return EMPTY;
6013            } else {
6014                // we found a common initial character sequence
6015                return strs[0].substring(0, smallestIndexOfDiff);
6016            }
6017        }
6018    
6019        // Misc
6020        //-----------------------------------------------------------------------
6021        /**
6022         * <p>Find the Levenshtein distance between two Strings.</p>
6023         *
6024         * <p>This is the number of changes needed to change one String into
6025         * another, where each change is a single character modification (deletion,
6026         * insertion or substitution).</p>
6027         *
6028         * <p>The previous implementation of the Levenshtein distance algorithm
6029         * was from <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p>
6030         *
6031         * <p>Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError
6032         * which can occur when my Java implementation is used with very large strings.<br>
6033         * This implementation of the Levenshtein distance algorithm
6034         * is from <a href="http://www.merriampark.com/ldjava.htm">http://www.merriampark.com/ldjava.htm</a></p>
6035         *
6036         * <pre>
6037         * StringUtils.getLevenshteinDistance(null, *)             = IllegalArgumentException
6038         * StringUtils.getLevenshteinDistance(*, null)             = IllegalArgumentException
6039         * StringUtils.getLevenshteinDistance("","")               = 0
6040         * StringUtils.getLevenshteinDistance("","a")              = 1
6041         * StringUtils.getLevenshteinDistance("aaapppp", "")       = 7
6042         * StringUtils.getLevenshteinDistance("frog", "fog")       = 1
6043         * StringUtils.getLevenshteinDistance("fly", "ant")        = 3
6044         * StringUtils.getLevenshteinDistance("elephant", "hippo") = 7
6045         * StringUtils.getLevenshteinDistance("hippo", "elephant") = 7
6046         * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8
6047         * StringUtils.getLevenshteinDistance("hello", "hallo")    = 1
6048         * </pre>
6049         *
6050         * @param s  the first String, must not be null
6051         * @param t  the second String, must not be null
6052         * @return result distance
6053         * @throws IllegalArgumentException if either String input {@code null}
6054         * @since 3.0 Changed signature from getLevenshteinDistance(String, String) to
6055         * getLevenshteinDistance(CharSequence, CharSequence)
6056         */
6057        public static int getLevenshteinDistance(CharSequence s, CharSequence t) {
6058            if (s == null || t == null) {
6059                throw new IllegalArgumentException("Strings must not be null");
6060            }
6061    
6062            /*
6063               The difference between this impl. and the previous is that, rather
6064               than creating and retaining a matrix of size s.length() + 1 by t.length() + 1,
6065               we maintain two single-dimensional arrays of length s.length() + 1.  The first, d,
6066               is the 'current working' distance array that maintains the newest distance cost
6067               counts as we iterate through the characters of String s.  Each time we increment
6068               the index of String t we are comparing, d is copied to p, the second int[].  Doing so
6069               allows us to retain the previous cost counts as required by the algorithm (taking
6070               the minimum of the cost count to the left, up one, and diagonally up and to the left
6071               of the current cost count being calculated).  (Note that the arrays aren't really
6072               copied anymore, just switched...this is clearly much better than cloning an array
6073               or doing a System.arraycopy() each time  through the outer loop.)
6074    
6075               Effectively, the difference between the two implementations is this one does not
6076               cause an out of memory condition when calculating the LD over two very large strings.
6077             */
6078    
6079            int n = s.length(); // length of s
6080            int m = t.length(); // length of t
6081    
6082            if (n == 0) {
6083                return m;
6084            } else if (m == 0) {
6085                return n;
6086            }
6087    
6088            if (n > m) {
6089                // swap the input strings to consume less memory
6090                CharSequence tmp = s;
6091                s = t;
6092                t = tmp;
6093                n = m;
6094                m = t.length();
6095            }
6096    
6097            int p[] = new int[n + 1]; //'previous' cost array, horizontally
6098            int d[] = new int[n + 1]; // cost array, horizontally
6099            int _d[]; //placeholder to assist in swapping p and d
6100    
6101            // indexes into strings s and t
6102            int i; // iterates through s
6103            int j; // iterates through t
6104    
6105            char t_j; // jth character of t
6106    
6107            int cost; // cost
6108    
6109            for (i = 0; i <= n; i++) {
6110                p[i] = i;
6111            }
6112    
6113            for (j = 1; j <= m; j++) {
6114                t_j = t.charAt(j - 1);
6115                d[0] = j;
6116    
6117                for (i = 1; i <= n; i++) {
6118                    cost = s.charAt(i - 1) == t_j ? 0 : 1;
6119                    // minimum of cell to the left+1, to the top+1, diagonally left and up +cost
6120                    d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
6121                }
6122    
6123                // copy current distance counts to 'previous row' distance counts
6124                _d = p;
6125                p = d;
6126                d = _d;
6127            }
6128    
6129            // our last action in the above loop was to switch d and p, so p now
6130            // actually has the most recent cost counts
6131            return p[n];
6132        }
6133    
6134        /**
6135         * <p>Find the Levenshtein distance between two Strings if it's less than or equal to a given 
6136         * threshold.</p>
6137         *
6138         * <p>This is the number of changes needed to change one String into
6139         * another, where each change is a single character modification (deletion,
6140         * insertion or substitution).</p>
6141         *
6142         * <p>This implementation follows from Algorithms on Strings, Trees and Sequences by Dan Gusfield
6143         * and Chas Emerick's implementation of the Levenshtein distance algorithm from
6144         * <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p>
6145         *
6146         * <pre>
6147         * StringUtils.getLevenshteinDistance(null, *, *)             = IllegalArgumentException
6148         * StringUtils.getLevenshteinDistance(*, null, *)             = IllegalArgumentException
6149         * StringUtils.getLevenshteinDistance(*, *, -1)               = IllegalArgumentException
6150         * StringUtils.getLevenshteinDistance("","", 0)               = 0
6151         * StringUtils.getLevenshteinDistance("aaapppp", "", 8)       = 7
6152         * StringUtils.getLevenshteinDistance("aaapppp", "", 7)       = 7
6153         * StringUtils.getLevenshteinDistance("aaapppp", "", 6))      = -1
6154         * StringUtils.getLevenshteinDistance("elephant", "hippo", 7) = 7
6155         * StringUtils.getLevenshteinDistance("elephant", "hippo", 6) = -1
6156         * StringUtils.getLevenshteinDistance("hippo", "elephant", 7) = 7
6157         * StringUtils.getLevenshteinDistance("hippo", "elephant", 6) = -1
6158         * </pre>
6159         *
6160         * @param s  the first String, must not be null
6161         * @param t  the second String, must not be null
6162         * @param threshold the target threshold, must not be negative
6163         * @return result distance, or {@code -1} if the distance would be greater than the threshold
6164         * @throws IllegalArgumentException if either String input {@code null} or negative threshold
6165         */
6166        public static int getLevenshteinDistance(CharSequence s, CharSequence t, int threshold) {
6167            if (s == null || t == null) {
6168                throw new IllegalArgumentException("Strings must not be null");
6169            }
6170            if (threshold < 0) {
6171                throw new IllegalArgumentException("Threshold must not be negative");
6172            }
6173    
6174            /*
6175            This implementation only computes the distance if it's less than or equal to the
6176            threshold value, returning -1 if it's greater.  The advantage is performance: unbounded
6177            distance is O(nm), but a bound of k allows us to reduce it to O(km) time by only 
6178            computing a diagonal stripe of width 2k + 1 of the cost table.
6179            It is also possible to use this to compute the unbounded Levenshtein distance by starting
6180            the threshold at 1 and doubling each time until the distance is found; this is O(dm), where
6181            d is the distance.
6182            
6183            One subtlety comes from needing to ignore entries on the border of our stripe
6184            eg.
6185            p[] = |#|#|#|*
6186            d[] =  *|#|#|#|
6187            We must ignore the entry to the left of the leftmost member
6188            We must ignore the entry above the rightmost member
6189            
6190            Another subtlety comes from our stripe running off the matrix if the strings aren't
6191            of the same size.  Since string s is always swapped to be the shorter of the two, 
6192            the stripe will always run off to the upper right instead of the lower left of the matrix.
6193            
6194            As a concrete example, suppose s is of length 5, t is of length 7, and our threshold is 1.
6195            In this case we're going to walk a stripe of length 3.  The matrix would look like so:
6196            
6197               1 2 3 4 5
6198            1 |#|#| | | |
6199            2 |#|#|#| | |
6200            3 | |#|#|#| |
6201            4 | | |#|#|#|
6202            5 | | | |#|#|
6203            6 | | | | |#|
6204            7 | | | | | |
6205    
6206            Note how the stripe leads off the table as there is no possible way to turn a string of length 5
6207            into one of length 7 in edit distance of 1.
6208            
6209            Additionally, this implementation decreases memory usage by using two 
6210            single-dimensional arrays and swapping them back and forth instead of allocating
6211            an entire n by m matrix.  This requires a few minor changes, such as immediately returning 
6212            when it's detected that the stripe has run off the matrix and initially filling the arrays with
6213            large values so that entries we don't compute are ignored.
6214    
6215            See Algorithms on Strings, Trees and Sequences by Dan Gusfield for some discussion.
6216             */
6217    
6218            int n = s.length(); // length of s
6219            int m = t.length(); // length of t
6220    
6221            // if one string is empty, the edit distance is necessarily the length of the other
6222            if (n == 0) {
6223                return m <= threshold ? m : -1;
6224            } else if (m == 0) {
6225                return n <= threshold ? n : -1;
6226            }
6227    
6228            if (n > m) {
6229                // swap the two strings to consume less memory
6230                CharSequence tmp = s;
6231                s = t;
6232                t = tmp;
6233                n = m;
6234                m = t.length();
6235            }
6236    
6237            int p[] = new int[n + 1]; // 'previous' cost array, horizontally
6238            int d[] = new int[n + 1]; // cost array, horizontally
6239            int _d[]; // placeholder to assist in swapping p and d
6240    
6241            // fill in starting table values
6242            int boundary = Math.min(n, threshold) + 1;
6243            for (int i = 0; i < boundary; i++) {
6244                p[i] = i;
6245            }
6246            // these fills ensure that the value above the rightmost entry of our 
6247            // stripe will be ignored in following loop iterations
6248            Arrays.fill(p, boundary, p.length, Integer.MAX_VALUE);
6249            Arrays.fill(d, Integer.MAX_VALUE);
6250    
6251            // iterates through t
6252            for (int j = 1; j <= m; j++) {
6253                char t_j = t.charAt(j - 1); // jth character of t
6254                d[0] = j;
6255    
6256                // compute stripe indices, constrain to array size
6257                int min = Math.max(1, j - threshold);
6258                int max = Math.min(n, j + threshold);
6259    
6260                // the stripe may lead off of the table if s and t are of different sizes
6261                if (min > max) {
6262                    return -1;
6263                }
6264    
6265                // ignore entry left of leftmost
6266                if (min > 1) {
6267                    d[min - 1] = Integer.MAX_VALUE;
6268                }
6269    
6270                // iterates through [min, max] in s
6271                for (int i = min; i <= max; i++) {
6272                    if (s.charAt(i - 1) == t_j) {
6273                        // diagonally left and up
6274                        d[i] = p[i - 1];
6275                    } else {
6276                        // 1 + minimum of cell to the left, to the top, diagonally left and up
6277                        d[i] = 1 + Math.min(Math.min(d[i - 1], p[i]), p[i - 1]);
6278                    }
6279                }
6280    
6281                // copy current distance counts to 'previous row' distance counts
6282                _d = p;
6283                p = d;
6284                d = _d;
6285            }
6286    
6287            // if p[n] is greater than the threshold, there's no guarantee on it being the correct
6288            // distance
6289            if (p[n] <= threshold) {
6290                return p[n];
6291            } else {
6292                return -1;
6293            }
6294        }
6295    
6296        // startsWith
6297        //-----------------------------------------------------------------------
6298    
6299        /**
6300         * <p>Check if a CharSequence starts with a specified prefix.</p>
6301         *
6302         * <p>{@code null}s are handled without exceptions. Two {@code null}
6303         * references are considered to be equal. The comparison is case sensitive.</p>
6304         *
6305         * <pre>
6306         * StringUtils.startsWith(null, null)      = true
6307         * StringUtils.startsWith(null, "abc")     = false
6308         * StringUtils.startsWith("abcdef", null)  = false
6309         * StringUtils.startsWith("abcdef", "abc") = true
6310         * StringUtils.startsWith("ABCDEF", "abc") = false
6311         * </pre>
6312         *
6313         * @see java.lang.String#startsWith(String)
6314         * @param str  the CharSequence to check, may be null
6315         * @param prefix the prefix to find, may be null
6316         * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or
6317         *  both {@code null}
6318         * @since 2.4
6319         * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence)
6320         */
6321        public static boolean startsWith(CharSequence str, CharSequence prefix) {
6322            return startsWith(str, prefix, false);
6323        }
6324    
6325        /**
6326         * <p>Case insensitive check if a CharSequence starts with a specified prefix.</p>
6327         *
6328         * <p>{@code null}s are handled without exceptions. Two {@code null}
6329         * references are considered to be equal. The comparison is case insensitive.</p>
6330         *
6331         * <pre>
6332         * StringUtils.startsWithIgnoreCase(null, null)      = true
6333         * StringUtils.startsWithIgnoreCase(null, "abc")     = false
6334         * StringUtils.startsWithIgnoreCase("abcdef", null)  = false
6335         * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
6336         * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
6337         * </pre>
6338         *
6339         * @see java.lang.String#startsWith(String)
6340         * @param str  the CharSequence to check, may be null
6341         * @param prefix the prefix to find, may be null
6342         * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or
6343         *  both {@code null}
6344         * @since 2.4
6345         * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence)
6346         */
6347        public static boolean startsWithIgnoreCase(CharSequence str, CharSequence prefix) {
6348            return startsWith(str, prefix, true);
6349        }
6350    
6351        /**
6352         * <p>Check if a CharSequence starts with a specified prefix (optionally case insensitive).</p>
6353         *
6354         * @see java.lang.String#startsWith(String)
6355         * @param str  the CharSequence to check, may be null
6356         * @param prefix the prefix to find, may be null
6357         * @param ignoreCase indicates whether the compare should ignore case
6358         *  (case insensitive) or not.
6359         * @return {@code true} if the CharSequence starts with the prefix or
6360         *  both {@code null}
6361         */
6362        private static boolean startsWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
6363            if (str == null || prefix == null) {
6364                return (str == null && prefix == null);
6365            }
6366            if (prefix.length() > str.length()) {
6367                return false;
6368            }
6369            return CharSequenceUtils.regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length());
6370        }
6371    
6372        /**
6373         * <p>Check if a CharSequence starts with any of an array of specified strings.</p>
6374         *
6375         * <pre>
6376         * StringUtils.startsWithAny(null, null)      = false
6377         * StringUtils.startsWithAny(null, new String[] {"abc"})  = false
6378         * StringUtils.startsWithAny("abcxyz", null)     = false
6379         * StringUtils.startsWithAny("abcxyz", new String[] {""}) = false
6380         * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true
6381         * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
6382         * </pre>
6383         *
6384         * @param string  the CharSequence to check, may be null
6385         * @param searchStrings the CharSequences to find, may be null or empty
6386         * @return {@code true} if the CharSequence starts with any of the the prefixes, case insensitive, or
6387         *  both {@code null}
6388         * @since 2.5
6389         * @since 3.0 Changed signature from startsWithAny(String, String[]) to startsWithAny(CharSequence, CharSequence...)
6390         */
6391        public static boolean startsWithAny(CharSequence string, CharSequence... searchStrings) {
6392            if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) {
6393                return false;
6394            }
6395            for (CharSequence searchString : searchStrings) {
6396                if (StringUtils.startsWith(string, searchString)) {
6397                    return true;
6398                }
6399            }
6400            return false;
6401        }
6402    
6403        // endsWith
6404        //-----------------------------------------------------------------------
6405    
6406        /**
6407         * <p>Check if a CharSequence ends with a specified suffix.</p>
6408         *
6409         * <p>{@code null}s are handled without exceptions. Two {@code null}
6410         * references are considered to be equal. The comparison is case sensitive.</p>
6411         *
6412         * <pre>
6413         * StringUtils.endsWith(null, null)      = true
6414         * StringUtils.endsWith(null, "def")     = false
6415         * StringUtils.endsWith("abcdef", null)  = false
6416         * StringUtils.endsWith("abcdef", "def") = true
6417         * StringUtils.endsWith("ABCDEF", "def") = false
6418         * StringUtils.endsWith("ABCDEF", "cde") = false
6419         * </pre>
6420         *
6421         * @see java.lang.String#endsWith(String)
6422         * @param str  the CharSequence to check, may be null
6423         * @param suffix the suffix to find, may be null
6424         * @return {@code true} if the CharSequence ends with the suffix, case sensitive, or
6425         *  both {@code null}
6426         * @since 2.4
6427         * @since 3.0 Changed signature from endsWith(String, String) to endsWith(CharSequence, CharSequence)
6428         */
6429        public static boolean endsWith(CharSequence str, CharSequence suffix) {
6430            return endsWith(str, suffix, false);
6431        }
6432    
6433        /**
6434         * <p>Case insensitive check if a CharSequence ends with a specified suffix.</p>
6435         *
6436         * <p>{@code null}s are handled without exceptions. Two {@code null}
6437         * references are considered to be equal. The comparison is case insensitive.</p>
6438         *
6439         * <pre>
6440         * StringUtils.endsWithIgnoreCase(null, null)      = true
6441         * StringUtils.endsWithIgnoreCase(null, "def")     = false
6442         * StringUtils.endsWithIgnoreCase("abcdef", null)  = false
6443         * StringUtils.endsWithIgnoreCase("abcdef", "def") = true
6444         * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true
6445         * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false
6446         * </pre>
6447         *
6448         * @see java.lang.String#endsWith(String)
6449         * @param str  the CharSequence to check, may be null
6450         * @param suffix the suffix to find, may be null
6451         * @return {@code true} if the CharSequence ends with the suffix, case insensitive, or
6452         *  both {@code null}
6453         * @since 2.4
6454         * @since 3.0 Changed signature from endsWithIgnoreCase(String, String) to endsWithIgnoreCase(CharSequence, CharSequence)
6455         */
6456        public static boolean endsWithIgnoreCase(CharSequence str, CharSequence suffix) {
6457            return endsWith(str, suffix, true);
6458        }
6459    
6460        /**
6461         * <p>Check if a CharSequence ends with a specified suffix (optionally case insensitive).</p>
6462         *
6463         * @see java.lang.String#endsWith(String)
6464         * @param str  the CharSequence to check, may be null
6465         * @param suffix the suffix to find, may be null
6466         * @param ignoreCase indicates whether the compare should ignore case
6467         *  (case insensitive) or not.
6468         * @return {@code true} if the CharSequence starts with the prefix or
6469         *  both {@code null}
6470         */
6471        private static boolean endsWith(CharSequence str, CharSequence suffix, boolean ignoreCase) {
6472            if (str == null || suffix == null) {
6473                return str == null && suffix == null;
6474            }
6475            if (suffix.length() > str.length()) {
6476                return false;
6477            }
6478            int strOffset = str.length() - suffix.length();
6479            return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length());
6480        }
6481    
6482        /**
6483         * <p>
6484         * Similar to <a
6485         * href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize
6486         * -space</a>
6487         * </p>
6488         * <p>
6489         * The function returns the argument string with whitespace normalized by using
6490         * <code>{@link #trim(String)}</code> to remove leading and trailing whitespace
6491         * and then replacing sequences of whitespace characters by a single space.
6492         * </p>
6493         * In XML Whitespace characters are the same as those allowed by the <a
6494         * href="http://www.w3.org/TR/REC-xml/#NT-S">S</a> production, which is S ::= (#x20 | #x9 | #xD | #xA)+
6495         * <p>
6496         * Java's regexp pattern \s defines whitespace as [ \t\n\x0B\f\r]
6497         * <p>
6498         * For reference:
6499         * <ul>
6500         * <li>\x0B = vertical tab</li>
6501         * <li>\f = #xC = form feed</li>
6502         * <li>#x20 = space</li>
6503         * <li>#x9 = \t</li>
6504         * <li>#xA = \n</li>
6505         * <li>#xD = \r</li>
6506         * </ul>
6507         * </p>
6508         * <p>
6509         * The difference is that Java's whitespace includes vertical tab and form feed, which this functional will also
6510         * normalize. Additionally <code>{@link #trim(String)}</code> removes control characters (char &lt;= 32) from both
6511         * ends of this String.
6512         * </p>
6513         *
6514         * @see Pattern
6515         * @see #trim(String)
6516         * @see <a
6517         *      href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize-space</a>
6518         * @param str the source String to normalize whitespaces from, may be null
6519         * @return the modified string with whitespace normalized, {@code null} if null String input
6520         *
6521         * @since 3.0
6522         */
6523        public static String normalizeSpace(String str) {
6524            if (str == null) {
6525                return null;
6526            }
6527            return WHITESPACE_BLOCK.matcher(trim(str)).replaceAll(" ");
6528        }
6529    
6530        /**
6531         * <p>Check if a CharSequence ends with any of an array of specified strings.</p>
6532         *
6533         * <pre>
6534         * StringUtils.endsWithAny(null, null)      = false
6535         * StringUtils.endsWithAny(null, new String[] {"abc"})  = false
6536         * StringUtils.endsWithAny("abcxyz", null)     = false
6537         * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true
6538         * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true
6539         * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
6540         * </pre>
6541         *
6542         * @param string  the CharSequence to check, may be null
6543         * @param searchStrings the CharSequences to find, may be null or empty
6544         * @return {@code true} if the CharSequence ends with any of the the prefixes, case insensitive, or
6545         *  both {@code null}
6546         * @since 3.0
6547         */
6548        public static boolean endsWithAny(CharSequence string, CharSequence... searchStrings) {
6549            if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) {
6550                return false;
6551            }
6552            for (CharSequence searchString : searchStrings) {
6553                if (StringUtils.endsWith(string, searchString)) {
6554                    return true;
6555                }
6556            }
6557            return false;
6558        }
6559    
6560    }