001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.search.highlight.HighlightUtil;
022    import com.liferay.portal.kernel.security.RandomUtil;
023    
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.InputStreamReader;
027    
028    import java.net.URL;
029    
030    import java.util.ArrayList;
031    import java.util.Collection;
032    import java.util.Enumeration;
033    import java.util.List;
034    import java.util.Locale;
035    import java.util.Map;
036    import java.util.Random;
037    import java.util.regex.Pattern;
038    
039    /**
040     * The String utility class.
041     *
042     * @author Brian Wing Shun Chan
043     * @author Sandeep Soni
044     * @author Ganesh Ram
045     * @author Shuyang Zhou
046     * @author Hugo Huijser
047     */
048    public class StringUtil {
049    
050            /**
051             * Adds string <code>add</code> to string <code>s</code> resulting in a
052             * comma delimited list of strings, disallowing duplicate strings in the
053             * list.
054             *
055             * <p>
056             * The resulting string ends with a comma even if the original string does
057             * not.
058             * </p>
059             *
060             * @param  s the original string, representing a comma delimited list of
061             *         strings
062             * @param  add the string to add to the original, representing the string to
063             *         add to the list
064             * @return a string that represents the original string and the added string
065             *         separated by a comma, or <code>null</code> if the string to add
066             *         is <code>null</code>
067             */
068            public static String add(String s, String add) {
069                    return add(s, add, StringPool.COMMA);
070            }
071    
072            /**
073             * Adds string <code>add</code> to string <code>s</code> that represents a
074             * delimited list of strings, using a specified delimiter and disallowing
075             * duplicate words.
076             *
077             * <p>
078             * The returned string ends with the delimiter even if the original string
079             * does not.
080             * </p>
081             *
082             * @param  s the original string, representing a delimited list of strings
083             * @param  add the string to add to the original, representing the string to
084             *         add to the list
085             * @param  delimiter the delimiter used to separate strings in the list
086             * @return a string that represents the original string and the added string
087             *         separated by the delimiter, or <code>null</code> if the string to
088             *         add or the delimiter string is <code>null</code>
089             */
090            public static String add(String s, String add, String delimiter) {
091                    return add(s, add, delimiter, false);
092            }
093    
094            /**
095             * Adds string <code>add</code> to string <code>s</code> that represents a
096             * delimited list of strings, using a specified delimiter and optionally
097             * allowing duplicate words.
098             *
099             * <p>
100             * The returned string ends with the delimiter even if the original string
101             * does not.
102             * </p>
103             *
104             * @param  s the original string, representing a delimited list of strings
105             * @param  add the string to add to the original, representing the string to
106             *         add to the list
107             * @param  delimiter the delimiter used to separate strings in the list
108             * @param  allowDuplicates whether to allow duplicate strings
109             * @return a string that represents the original string and the added string
110             *         separated by the delimiter, or <code>null</code> if the string to
111             *         add or the delimiter string is <code>null</code>
112             */
113            public static String add(
114                    String s, String add, String delimiter, boolean allowDuplicates) {
115    
116                    if ((add == null) || (delimiter == null)) {
117                            return null;
118                    }
119    
120                    if (s == null) {
121                            s = StringPool.BLANK;
122                    }
123    
124                    if (allowDuplicates || !contains(s, add, delimiter)) {
125                            StringBundler sb = new StringBundler(4);
126    
127                            sb.append(s);
128    
129                            if (Validator.isNull(s) || s.endsWith(delimiter)) {
130                                    sb.append(add);
131                                    sb.append(delimiter);
132                            }
133                            else {
134                                    sb.append(delimiter);
135                                    sb.append(add);
136                                    sb.append(delimiter);
137                            }
138    
139                            s = sb.toString();
140                    }
141    
142                    return s;
143            }
144    
145            /**
146             * Returns the original string with an appended space followed by the string
147             * value of the suffix surrounded by parentheses.
148             *
149             * <p>
150             * If the original string ends with a numerical parenthetical suffix having
151             * an integer value equal to <code>suffix - 1</code>, then the existing
152             * parenthetical suffix is replaced by the new one.
153             * </p>
154             *
155             * <p>
156             * Examples:
157             * </p>
158             *
159             * <p>
160             * <pre>
161             * <code>
162             * appendParentheticalSuffix("file", 0) returns "file (0)"
163             * appendParentheticalSuffix("file (0)", 0) returns "file (0) (0)"
164             * appendParentheticalSuffix("file (0)", 1) returns "file (1)"
165             * appendParentheticalSuffix("file (0)", 2) returns "file (0) (2)"
166             * </code>
167             * </pre>
168             * </p>
169             *
170             * @param  s the original string
171             * @param  suffix the suffix to be appended
172             * @return the resultant string whose characters equal those of the original
173             *         string, followed by a space, followed by the specified suffix
174             *         enclosed in parentheses, or, if the difference between the
175             *         provided suffix and the existing suffix is 1, the existing suffix
176             *         is incremented by 1
177             */
178            public static String appendParentheticalSuffix(String s, int suffix) {
179                    if (Pattern.matches(".* \\(" + String.valueOf(suffix - 1) + "\\)", s)) {
180                            int pos = s.lastIndexOf(" (");
181    
182                            s = s.substring(0, pos);
183                    }
184    
185                    return appendParentheticalSuffix(s, String.valueOf(suffix));
186            }
187    
188            /**
189             * Returns the original string with an appended space followed by the suffix
190             * surrounded by parentheses.
191             *
192             * <p>
193             * Example:
194             * </p>
195             *
196             * <p>
197             * <pre>
198             * <code>
199             * appendParentheticalSuffix("Java", "EE") returns "Java (EE)"
200             * </code>
201             * </pre>
202             * </p>
203             *
204             * @param  s the original string
205             * @param  suffix the suffix to be appended
206             * @return a string that represents the original string, followed by a
207             *         space, followed by the suffix enclosed in parentheses
208             */
209            public static String appendParentheticalSuffix(String s, String suffix) {
210                    StringBundler sb = new StringBundler(5);
211    
212                    sb.append(s);
213                    sb.append(StringPool.SPACE);
214                    sb.append(StringPool.OPEN_PARENTHESIS);
215                    sb.append(suffix);
216                    sb.append(StringPool.CLOSE_PARENTHESIS);
217    
218                    return sb.toString();
219            }
220    
221            /**
222             * Converts an array of bytes to a string representing the bytes in
223             * hexadecimal form.
224             *
225             * @param  bytes the array of bytes to be converted
226             * @return the string representing the bytes in hexadecimal form
227             */
228            public static String bytesToHexString(byte[] bytes) {
229                    char[] chars = new char[bytes.length * 2];
230    
231                    for (int i = 0; i < bytes.length; i++) {
232                            chars[i * 2] = HEX_DIGITS[(bytes[i] & 0xFF) >> 4];
233                            chars[i * 2 + 1] = HEX_DIGITS[bytes[i] & 0x0F];
234                    }
235    
236                    return new String(chars);
237            }
238    
239            /**
240             * Returns <code>true</code> if the string contains the text as one or more
241             * consecutive comma delimited list entries.
242             *
243             * <p>
244             * Examples:
245             * </p>
246             *
247             * <p>
248             * <pre>
249             * <code>
250             * contains("one,two,three", "two") returns true
251             * contains("one,two,three", "thr") returns false
252             * contains("one,two,three", "one,two") returns true
253             * </code>
254             * </pre>
255             * </p>
256             *
257             * @param  s the string in which to search
258             * @param  text the text to search for in the string
259             * @return <code>true</code> if the string contains the text as one or more
260             *         consecutive comma delimited list entries; <code>false</code>
261             *         otherwise
262             */
263            public static boolean contains(String s, String text) {
264                    return contains(s, text, StringPool.COMMA);
265            }
266    
267            /**
268             * Returns <code>true</code> if the string contains the text as one or more
269             * consecutive delimited list entries.
270             *
271             * <p>
272             * Examples:
273             * </p>
274             *
275             * <p>
276             * <pre>
277             * <code>
278             * contains("three...two...one", "two", "...") returns true
279             * contains("three...two...one", "thr", "...") returns false
280             * contains("three...two...one", "two...one", "...") returns true
281             * </code>
282             * </pre>
283             * </p>
284             *
285             * @param  s the string in which to search
286             * @param  text the text to search for in the string
287             * @param  delimiter the delimiter
288             * @return <code>true</code> if the string contains the text as one or more
289             *         consecutive delimited list entries; <code>false</code> otherwise
290             */
291            public static boolean contains(String s, String text, String delimiter) {
292                    if ((s == null) || (text == null) || (delimiter == null)) {
293                            return false;
294                    }
295    
296                    if (!s.endsWith(delimiter)) {
297                            s = s.concat(delimiter);
298                    }
299    
300                    String dtd = delimiter.concat(text).concat(delimiter);
301    
302                    int pos = s.indexOf(dtd);
303    
304                    if (pos == -1) {
305                            String td = text.concat(delimiter);
306    
307                            if (s.startsWith(td)) {
308                                    return true;
309                            }
310    
311                            return false;
312                    }
313    
314                    return true;
315            }
316    
317            /**
318             * Returns <code>true</code> if the string contains the text as one or more
319             * consecutive comma delimited list entries, ignoring case.
320             *
321             * <p>
322             * Examples:
323             * </p>
324             *
325             * <p>
326             * <pre>
327             * <code>
328             * containsIgnoreCase("one,two,three", "Two") returns true
329             * containsIgnoreCase("one,two,three", "thr") returns false
330             * containsIgnoreCase("one,two,three", "one,two") returns true
331             * </code>
332             * </pre>
333             * </p>
334             *
335             * @param  s the string in which to search
336             * @param  text the text to search for in the string
337             * @return <code>true</code> if the string contains the text as one or more
338             *         consecutive comma delimited list entries; <code>false</code>
339             *         otherwise
340             */
341            public static boolean containsIgnoreCase(String s, String text) {
342                    return containsIgnoreCase(s, text, StringPool.COMMA);
343            }
344    
345            /**
346             * Returns <code>true</code> if the string contains the text as one or more
347             * consecutive delimited list entries, ignoring case.
348             *
349             * <p>
350             * Examples:
351             * </p>
352             *
353             * <p>
354             * <pre>
355             * <code>
356             * containsIgnoreCase("three...two...one", "Two", "...") returns true
357             * containsIgnoreCase("three...two...one", "thr", "...") returns false
358             * containsIgnoreCase("three...two...one", "two...one", "...") returns true
359             * </code>
360             * </pre>
361             * </p>
362             *
363             * @param  s the string in which to search
364             * @param  text the text to search for in the string
365             * @param  delimiter the delimiter
366             * @return <code>true</code> if the string contains the text as one or more
367             *         consecutive delimited list entries; <code>false</code> otherwise
368             */
369            public static boolean containsIgnoreCase(
370                    String s, String text, String delimiter) {
371    
372                    if ((s == null) || (text == null) || (delimiter == null)) {
373                            return false;
374                    }
375    
376                    return contains(toLowerCase(s), toLowerCase(text), delimiter);
377            }
378    
379            /**
380             * Returns the number of times the text appears in the string.
381             *
382             * @param  s the string in which to search
383             * @param  text the text to search for in the string
384             * @return the number of times the text appears in the string
385             */
386            public static int count(String s, String text) {
387                    if ((s == null) || (s.length() == 0) || (text == null) ||
388                            (text.length() == 0)) {
389    
390                            return 0;
391                    }
392    
393                    int count = 0;
394    
395                    int pos = s.indexOf(text);
396    
397                    while (pos != -1) {
398                            pos = s.indexOf(text, pos + text.length());
399    
400                            count++;
401                    }
402    
403                    return count;
404            }
405    
406            /**
407             * Returns <code>true</code> if the string ends with the specified
408             * character, ignoring case.
409             *
410             * @param  s the string in which to search
411             * @param  end the character to search for at the end of the string
412             * @return <code>true</code> if the string ends with the specified
413             *         character, ignoring case; <code>false</code> otherwise
414             */
415            public static boolean endsWith(String s, char end) {
416                    return endsWith(s, (Character.valueOf(end)).toString());
417            }
418    
419            /**
420             * Returns <code>true</code> if the string ends with the string
421             * <code>end</code>, ignoring case.
422             *
423             * @param  s the string in which to search
424             * @param  end the string to check for at the end of the string
425             * @return <code>true</code> if the string ends with the string
426             *         <code>end</code>, ignoring case; <code>false</code> otherwise
427             */
428            public static boolean endsWith(String s, String end) {
429                    if ((s == null) || (end == null)) {
430                            return false;
431                    }
432    
433                    if (end.length() > s.length()) {
434                            return false;
435                    }
436    
437                    String temp = s.substring(s.length() - end.length());
438    
439                    if (equalsIgnoreCase(temp, end)) {
440                            return true;
441                    }
442                    else {
443                            return false;
444                    }
445            }
446    
447            /**
448             * Returns <code>true</code> if the strings are equal, ignoring new line
449             * characters.
450             *
451             * @param  s1 the first string to compare
452             * @param  s2 the second string to compare
453             * @return <code>true</code> if the strings are equal, ignoring new line
454             *         characters; <code>false</code> otherwise
455             */
456            public static boolean equalsIgnoreBreakLine(String s1, String s2) {
457                    if (s1 == s2) {
458                            return true;
459                    }
460    
461                    if ((s1 == null) || (s2 == null)) {
462                            return false;
463                    }
464    
465                    s1 = replace(
466                            s1, new String[] {StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE},
467                            new String[] {StringPool.BLANK, StringPool.BLANK});
468                    s2 = replace(
469                            s2, new String[] {StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE},
470                            new String[] {StringPool.BLANK, StringPool.BLANK});
471    
472                    if (s1.length() != s2.length()) {
473                            return false;
474                    }
475    
476                    return s1.equals(s2);
477            }
478    
479            /**
480             * Returns <code>true</code> if the strings are equal, ignoring case.
481             *
482             * @param  s1 the first string to compare
483             * @param  s2 the second string to compare
484             * @return <code>true</code> if the strings are equal, ignoring case;
485             *         <code>false</code> otherwise
486             */
487            public static boolean equalsIgnoreCase(String s1, String s2) {
488                    if (s1 == s2) {
489                            return true;
490                    }
491    
492                    if ((s1 == null) || (s2 == null)) {
493                            return false;
494                    }
495    
496                    if (s1.length() != s2.length()) {
497                            return false;
498                    }
499    
500                    for (int i = 0; i < s1.length(); i++) {
501                            char c1 = s1.charAt(i);
502    
503                            char c2 = s2.charAt(i);
504    
505                            if (c1 == c2) {
506                                    continue;
507                            }
508    
509                            // Fast fallback for non-acsii code.
510    
511                            if ((c1 > 127) || (c2 > 127)) {
512    
513                                    // Georgian alphabet needs to check both upper and lower case
514    
515                                    if ((Character.toLowerCase(c1) == Character.toLowerCase(c2)) ||
516                                            (Character.toUpperCase(c1) == Character.toUpperCase(c2))) {
517    
518                                            continue;
519                                    }
520    
521                                    return false;
522                            }
523    
524                            // Fast fallback for non-letter ascii code
525    
526                            if ((c1 < CharPool.UPPER_CASE_A) || (c1 > CharPool.LOWER_CASE_Z) ||
527                                    (c2 < CharPool.UPPER_CASE_A) || (c2 > CharPool.LOWER_CASE_Z)) {
528    
529                                    return false;
530                            }
531    
532                            int delta = c1 - c2;
533    
534                            if ((delta != 32) && (delta != -32)) {
535                                    return false;
536                            }
537                    }
538    
539                    return true;
540            }
541    
542            /**
543             * Returns the substring of each character instance in string <code>s</code>
544             * that is found in the character array <code>chars</code>. The substring of
545             * characters returned maintain their original order.
546             *
547             * @param  s the string from which to extract characters
548             * @param  chars the characters to extract from the string
549             * @return the substring of each character instance in string <code>s</code>
550             *         that is found in the character array <code>chars</code>, or an
551             *         empty string if the given string is <code>null</code>
552             */
553            public static String extract(String s, char[] chars) {
554                    if (s == null) {
555                            return StringPool.BLANK;
556                    }
557    
558                    StringBundler sb = new StringBundler();
559    
560                    for (char c1 : s.toCharArray()) {
561                            for (char c2 : chars) {
562                                    if (c1 == c2) {
563                                            sb.append(c1);
564    
565                                            break;
566                                    }
567                            }
568                    }
569    
570                    return sb.toString();
571            }
572    
573            /**
574             * Returns the substring of English characters from the string.
575             *
576             * @param  s the string from which to extract characters
577             * @return the substring of English characters from the string, or an empty
578             *         string if the given string is <code>null</code>
579             */
580            public static String extractChars(String s) {
581                    if (s == null) {
582                            return StringPool.BLANK;
583                    }
584    
585                    StringBundler sb = new StringBundler();
586    
587                    char[] chars = s.toCharArray();
588    
589                    for (char c : chars) {
590                            if (Validator.isChar(c)) {
591                                    sb.append(c);
592                            }
593                    }
594    
595                    return sb.toString();
596            }
597    
598            /**
599             * Returns a string consisting of all of the digits extracted from the
600             * string.
601             *
602             * @param  s the string from which to extract digits
603             * @return a string consisting of all of the digits extracted from the
604             *         string
605             */
606            public static String extractDigits(String s) {
607                    if (s == null) {
608                            return StringPool.BLANK;
609                    }
610    
611                    StringBundler sb = new StringBundler();
612    
613                    char[] chars = s.toCharArray();
614    
615                    for (char c : chars) {
616                            if (Validator.isDigit(c)) {
617                                    sb.append(c);
618                            }
619                    }
620    
621                    return sb.toString();
622            }
623    
624            /**
625             * Returns the substring of <code>s</code> up to but not including the first
626             * occurrence of the delimiter.
627             *
628             * @param  s the string from which to extract a substring
629             * @param  delimiter the character whose index in the string marks where to
630             *         end the substring
631             * @return the substring of <code>s</code> up to but not including the first
632             *         occurrence of the delimiter, <code>null</code> if the string is
633             *         <code>null</code> or the delimiter does not occur in the string
634             */
635            public static String extractFirst(String s, char delimiter) {
636                    if (s == null) {
637                            return null;
638                    }
639    
640                    int index = s.indexOf(delimiter);
641    
642                    if (index < 0) {
643                            return null;
644                    }
645                    else {
646                            return s.substring(0, index);
647                    }
648            }
649    
650            /**
651             * Returns the substring of <code>s</code> up to but not including the first
652             * occurrence of the delimiter.
653             *
654             * @param  s the string from which to extract a substring
655             * @param  delimiter the smaller string whose index in the larger string
656             *         marks where to end the substring
657             * @return the substring of <code>s</code> up to but not including the first
658             *         occurrence of the delimiter, <code>null</code> if the string is
659             *         <code>null</code> or the delimiter does not occur in the string
660             */
661            public static String extractFirst(String s, String delimiter) {
662                    if (s == null) {
663                            return null;
664                    }
665    
666                    int index = s.indexOf(delimiter);
667    
668                    if (index < 0) {
669                            return null;
670                    }
671                    else {
672                            return s.substring(0, index);
673                    }
674            }
675    
676            /**
677             * Returns the substring of <code>s</code> after but not including the last
678             * occurrence of the delimiter.
679             *
680             * @param  s the string from which to extract the substring
681             * @param  delimiter the character whose last index in the string marks
682             *         where to begin the substring
683             * @return the substring of <code>s</code> after but not including the last
684             *         occurrence of the delimiter, <code>null</code> if the string is
685             *         <code>null</code> or the delimiter does not occur in the string
686             */
687            public static String extractLast(String s, char delimiter) {
688                    if (s == null) {
689                            return null;
690                    }
691    
692                    int index = s.lastIndexOf(delimiter);
693    
694                    if (index < 0) {
695                            return null;
696                    }
697                    else {
698                            return s.substring(index + 1);
699                    }
700            }
701    
702            /**
703             * Returns the substring of <code>s</code> after but not including the last
704             * occurrence of the delimiter.
705             *
706             * @param  s the string from which to extract the substring
707             * @param  delimiter the string whose last index in the string marks where
708             *         to begin the substring
709             * @return the substring of <code>s</code> after but not including the last
710             *         occurrence of the delimiter, <code>null</code> if the string is
711             *         <code>null</code> or the delimiter does not occur in the string
712             */
713            public static String extractLast(String s, String delimiter) {
714                    if (s == null) {
715                            return null;
716                    }
717    
718                    int index = s.lastIndexOf(delimiter);
719    
720                    if (index < 0) {
721                            return null;
722                    }
723                    else {
724                            return s.substring(index + delimiter.length());
725                    }
726            }
727    
728            /**
729             * Returns the substring of all leading digits of string <code>s</code>, or
730             * an empty string if it has no leading digits.
731             *
732             * @param  s the string from which to extract the substring
733             * @return the substring of all leading digits of string <code>s</code>, or
734             *         an empty string if it has no leading digits
735             */
736            public static String extractLeadingDigits(String s) {
737                    if (s == null) {
738                            return StringPool.BLANK;
739                    }
740    
741                    StringBundler sb = new StringBundler();
742    
743                    char[] chars = s.toCharArray();
744    
745                    for (char c : chars) {
746                            if (Validator.isDigit(c)) {
747                                    sb.append(c);
748                            }
749                            else {
750                                    return sb.toString();
751                            }
752                    }
753    
754                    return sb.toString();
755            }
756    
757            /**
758             * @deprecated As of 7.0.0, moved to {@link HighlightUtil#highlight(String,
759             *             String[])}}
760             */
761            @Deprecated
762            public static String highlight(String s, String[] queryTerms) {
763                    return HighlightUtil.highlight(s, queryTerms);
764            }
765    
766            /**
767             * @deprecated As of 7.0.0, moved to {@link HighlightUtil#highlight(String,
768             *             String[], String, String)}}
769             */
770            @Deprecated
771            public static String highlight(
772                    String s, String[] queryTerms, String highlight1, String highlight2) {
773    
774                    return HighlightUtil.highlight(s, queryTerms, highlight1, highlight2);
775            }
776    
777            /**
778             * Returns the index within the string of the first occurrence of any
779             * character from the array.
780             *
781             * <p>
782             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
783             * or empty array returns <code>-1</code>.
784             * </p>
785             *
786             * <p>
787             * Examples:
788             * </p>
789             *
790             * <p>
791             * <pre>
792             * <code>
793             * indexOfAny(null, *) returns -1
794             * indexOfAny(*, null) returns -1
795             * indexOfAny(*, []) returns -1
796             * indexOfAny("zzabyycdxx", ['a','c']) returns 2
797             * indexOfAny("zzabyycdxx", ['c','a']) returns 2
798             * indexOfAny("zzabyycdxx", ['m','n']) returns -1
799             * </code>
800             * </pre>
801             * </p>
802             *
803             * @param  s the string to search (optionally <code>null</code>)
804             * @param  chars the characters to search for (optionally <code>null</code>)
805             * @return the index within the string of the first occurrence of any
806             *         character from the array, or <code>-1</code> if none of the
807             *         characters occur
808             */
809            public static int indexOfAny(String s, char[] chars) {
810                    if (s == null) {
811                            return -1;
812                    }
813    
814                    return indexOfAny(s, chars, 0, s.length() - 1);
815            }
816    
817            /**
818             * Returns the index within the string of the first occurrence of any
819             * character from the array, starting the search at the specified index
820             * within the string.
821             *
822             * <p>
823             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
824             * or empty array returns <code>-1</code>.
825             * </p>
826             *
827             * <p>
828             * Examples:
829             * </p>
830             *
831             * <p>
832             * <pre>
833             * <code>
834             * indexOfAny(null, *, *) returns -1
835             * indexOfAny(*, null, *) returns -1
836             * indexOfAny(*, [], *) returns -1
837             * indexOfAny("zzabyycdxx", ['a','c'], 3) returns 6
838             * </code>
839             * </pre>
840             * </p>
841             *
842             * @param  s the string to search (optionally <code>null</code>)
843             * @param  chars the characters to search for (optionally <code>null</code>)
844             * @param  fromIndex the start index within the string
845             * @return the index within the string of the first occurrence of any
846             *         character from the array, starting the search at the specified
847             *         index within the string, or <code>-1</code> if none of the
848             *         characters occur
849             */
850            public static int indexOfAny(String s, char[] chars, int fromIndex) {
851                    if (s == null) {
852                            return -1;
853                    }
854    
855                    return indexOfAny(s, chars, fromIndex, s.length() - 1);
856            }
857    
858            /**
859             * Returns the index within the string of the first occurrence of any
860             * character from the array, up to and including the specified end index
861             * within the string, starting the search at the specified start index
862             * within the string.
863             *
864             * <p>
865             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
866             * or empty array returns <code>-1</code>.
867             * </p>
868             *
869             * <p>
870             * Examples:
871             * </p>
872             *
873             * <p>
874             * <pre>
875             * <code>
876             * indexOfAny(null, *, *, *) returns -1
877             * indexOfAny(*, null, *, *) returns -1
878             * indexOfAny(*, [], *, *) returns -1
879             * indexOfAny("zzabyycdxx", ['a','c'], 3, 7) returns 6
880             * </code>
881             * </pre>
882             * </p>
883             *
884             * @param  s the string to search (optionally <code>null</code>)
885             * @param  chars the characters to search for (optionally <code>null</code>)
886             * @param  fromIndex the start index within the string
887             * @param  toIndex the end index within the string
888             * @return the index within the string of the first occurrence of any
889             *         character from the array, up to and including the specified end
890             *         index within the string, starting the search at the specified
891             *         start index within the string, or <code>-1</code> if none of the
892             *         characters occur
893             */
894            public static int indexOfAny(
895                    String s, char[] chars, int fromIndex, int toIndex) {
896    
897                    if ((s == null) || (toIndex < fromIndex)) {
898                            return -1;
899                    }
900    
901                    if (ArrayUtil.isEmpty(chars)) {
902                            return -1;
903                    }
904    
905                    if (fromIndex >= s.length()) {
906                            return -1;
907                    }
908    
909                    if (fromIndex < 0) {
910                            fromIndex = 0;
911                    }
912    
913                    if (toIndex >= s.length()) {
914                            toIndex = s.length() - 1;
915                    }
916    
917                    for (int i = fromIndex; i <= toIndex; i++) {
918                            char c = s.charAt(i);
919    
920                            for (int j = 0; j < chars.length; j++) {
921                                    if (c == chars[j]) {
922                                            return i;
923                                    }
924                            }
925                    }
926    
927                    return -1;
928            }
929    
930            /**
931             * Returns the index within the string of the first occurrence of any string
932             * from the array.
933             *
934             * <p>
935             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
936             * or empty array returns <code>-1</code>, but an array containing
937             * <code>""</code> returns <code>0</code> if the string is not
938             * <code>null</code>.
939             * </p>
940             *
941             * <p>
942             * Examples:
943             * </p>
944             *
945             * <p>
946             * <pre>
947             * <code>
948             * indexOfAny(null, *) returns -1
949             * indexOfAny(*, null) returns -1
950             * indexOfAny(*, [null]) returns -1
951             * indexOfAny(*, []) returns -1
952             * indexOfAny("zzabyycdxx", ["ab","cd"]) returns 2
953             * indexOfAny("zzabyycdxx", ["cd","ab"]) returns 2
954             * indexOfAny("zzabyycdxx", ["mn","op"]) returns -1
955             * indexOfAny("zzabyycdxx", ["mn",""]) returns 0
956             * </code>
957             * </pre>
958             * </p>
959             *
960             * @param  s the string (optionally <code>null</code>)
961             * @param  texts the strings to search for (optionally <code>null</code>)
962             * @return the index within the string of the first occurrence of any string
963             *         from the array, <code>0</code> if the search array contains
964             *         <code>""</code>, or <code>-1</code> if none of the strings occur
965             */
966            public static int indexOfAny(String s, String[] texts) {
967                    if (s == null) {
968                            return -1;
969                    }
970    
971                    return indexOfAny(s, texts, 0, s.length() - 1);
972            }
973    
974            /**
975             * Returns the index within the string of the first occurrence of any string
976             * from the array, starting the search at the specified index within the
977             * string.
978             *
979             * <p>
980             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
981             * or empty array returns <code>-1</code>, but an array containing
982             * <code>""</code> returns the specified start index if the string is not
983             * <code>null</code>.
984             * </p>
985             *
986             * <p>
987             * Examples:
988             * </p>
989             *
990             * <p>
991             * <pre>
992             * <code>
993             * indexOfAny(null, *, *) returns -1
994             * indexOfAny(*, null, *) returns -1
995             * indexOfAny(*, [null], *) returns -1
996             * indexOfAny(*, [], *) returns -1
997             * indexOfAny("zzabyycdxx", ["ab","cd"], 3) returns 6
998             * indexOfAny("zzabyycdxx", ["cd","ab"], 3) returns 6
999             * indexOfAny("zzabyycdxx", ["mn","op"], *) returns -1
1000             * indexOfAny("zzabyycdxx", ["mn",""], 3) returns 3
1001             * </code>
1002             * </pre>
1003             * </p>
1004             *
1005             * @param  s the string to search (optionally <code>null</code>)
1006             * @param  texts the strings to search for (optionally <code>null</code>)
1007             * @param  fromIndex the start index within the string
1008             * @return the index within the string of the first occurrence of any string
1009             *         from the array, starting the search at the specified index within
1010             *         the string, the start index if the search array contains
1011             *         <code>""</code>, or <code>-1</code> if none of the strings occur
1012             */
1013            public static int indexOfAny(String s, String[] texts, int fromIndex) {
1014                    if (s == null) {
1015                            return -1;
1016                    }
1017    
1018                    return indexOfAny(s, texts, fromIndex, s.length() - 1);
1019            }
1020    
1021            /**
1022             * Returns the index within the string of the first occurrence of any string
1023             * from the array, up to and including the specified end index within the
1024             * string, starting the search at the specified start index within the
1025             * string.
1026             *
1027             * <p>
1028             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1029             * or empty array returns <code>-1</code>, but an array containing
1030             * <code>""</code> returns the specified start index if the string is not
1031             * <code>null</code>.
1032             * </p>
1033             *
1034             * <p>
1035             * Examples:
1036             * </p>
1037             *
1038             * <p>
1039             * <pre>
1040             * <code>
1041             * indexOfAny(null, *, *, *) returns -1
1042             * indexOfAny(*, null, *, *) returns -1
1043             * indexOfAny(*, [null], *, *) returns -1
1044             * indexOfAny(*, [], *, *) returns -1
1045             * indexOfAny("zzabyycdxx", ["ab","cd"], 3, 7) returns 6
1046             * indexOfAny("zzabyycdxx", ["cd","ab"], 2, 7) returns 2
1047             * indexOfAny("zzabyycdxx", ["mn","op"], *, *) returns -1
1048             * indexOfAny("zzabyycdxx", ["mn",""], 3, *) returns 3
1049             * </code>
1050             * </pre>
1051             * </p>
1052             *
1053             * @param  s the string to search (optionally <code>null</code>)
1054             * @param  texts the strings to search for (optionally <code>null</code>)
1055             * @param  fromIndex the start index within the string
1056             * @param  toIndex the end index within the string
1057             * @return the index within the string of the first occurrence of any string
1058             *         from the array, up to and including the specified end index
1059             *         within the string, starting the search at the specified start
1060             *         index within the string, the start index if the search array
1061             *         contains <code>""</code>, or <code>-1</code> if none of the
1062             *         strings occur
1063             */
1064            public static int indexOfAny(
1065                    String s, String[] texts, int fromIndex, int toIndex) {
1066    
1067                    if ((s == null) || (toIndex < fromIndex)) {
1068                            return -1;
1069                    }
1070    
1071                    if (ArrayUtil.isEmpty(texts)) {
1072                            return -1;
1073                    }
1074    
1075                    if (fromIndex >= s.length()) {
1076                            return -1;
1077                    }
1078    
1079                    if (fromIndex < 0) {
1080                            fromIndex = 0;
1081                    }
1082    
1083                    if (toIndex >= s.length()) {
1084                            toIndex = s.length() - 1;
1085                    }
1086    
1087                    for (int i = fromIndex; i <= toIndex; i++) {
1088                            for (int j = 0; j < texts.length; j++) {
1089                                    if (texts[j] == null) {
1090                                            continue;
1091                                    }
1092    
1093                                    if ((i + texts[j].length() <= toIndex + 1) &&
1094                                            s.startsWith(texts[j], i)) {
1095    
1096                                            return i;
1097                                    }
1098                            }
1099                    }
1100    
1101                    return -1;
1102            }
1103    
1104            /**
1105             * Inserts one string into the other at the specified offset index.
1106             *
1107             * @param  s the original string
1108             * @param  insert the string to be inserted into the original string
1109             * @param  offset the index of the original string where the insertion
1110             *         should take place
1111             * @return a string representing the original string with the other string
1112             *         inserted at the specified offset index, or <code>null</code> if
1113             *         the original string is <code>null</code>
1114             */
1115            public static String insert(String s, String insert, int offset) {
1116                    if (s == null) {
1117                            return null;
1118                    }
1119    
1120                    if (insert == null) {
1121                            return s;
1122                    }
1123    
1124                    if (offset > s.length()) {
1125                            return s.concat(insert);
1126                    }
1127    
1128                    String prefix = s.substring(0, offset);
1129                    String postfix = s.substring(offset);
1130    
1131                    return prefix.concat(insert).concat(postfix);
1132            }
1133    
1134            /**
1135             * Returns <code>true</code> if all the characters in string <code>s</code>
1136             * are lower case, ignoring any non-alphabetic characters.
1137             *
1138             * @param  s the string in which to search
1139             * @return <code>true</code> if all the characters in string <code>s</code>
1140             *         are lower case, ignoring any non-alphabetic characters;
1141             *         <code>false</code> otherwise
1142             */
1143            public static boolean isLowerCase(String s) {
1144                    if (s == null) {
1145                            return false;
1146                    }
1147    
1148                    for (int i = 0; i < s.length(); i++) {
1149                            char c = s.charAt(i);
1150    
1151                            // Fast path for ascii code, fallback to the slow unicode detection
1152    
1153                            if (c <= 127) {
1154                                    if ((c >= CharPool.UPPER_CASE_A) &&
1155                                            (c <= CharPool.UPPER_CASE_Z)) {
1156    
1157                                            return false;
1158                                    }
1159    
1160                                    continue;
1161                            }
1162    
1163                            if (Character.isLetter(c) && Character.isUpperCase(c)) {
1164                                    return false;
1165                            }
1166                    }
1167    
1168                    return true;
1169            }
1170    
1171            /**
1172             * Returns <code>true</code> if all the characters in string <code>s</code>
1173             * are upper case, ignoring any non-alphabetic characters.
1174             *
1175             * @param  s the string in which to search
1176             * @return <code>true</code> if all the characters in string <code>s</code>
1177             *         are upper case, ignoring any non-alphabetic characters;
1178             *         <code>false</code> otherwise
1179             */
1180            public static boolean isUpperCase(String s) {
1181                    if (s == null) {
1182                            return false;
1183                    }
1184    
1185                    for (int i = 0; i < s.length(); i++) {
1186                            char c = s.charAt(i);
1187    
1188                            // Fast path for ascii code, fallback to the slow unicode detection
1189    
1190                            if (c <= 127) {
1191                                    if ((c >= CharPool.LOWER_CASE_A) &&
1192                                            (c <= CharPool.LOWER_CASE_Z)) {
1193    
1194                                            return false;
1195                                    }
1196    
1197                                    continue;
1198                            }
1199    
1200                            if (Character.isLetter(c) && Character.isLowerCase(c)) {
1201                                    return false;
1202                            }
1203                    }
1204    
1205                    return true;
1206            }
1207    
1208            /**
1209             * Returns the index within the string of the last occurrence of any
1210             * character from the array.
1211             *
1212             * <p>
1213             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1214             * or empty array returns <code>-1</code>.
1215             * </p>
1216             *
1217             * <p>
1218             * Examples:
1219             * </p>
1220             *
1221             * <p>
1222             * <pre>
1223             * <code>
1224             * lastIndexOfAny(null, *) returns -1
1225             * lastIndexOfAny(*, null) returns -1
1226             * lastIndexOfAny(*, []) returns -1
1227             * lastIndexOfAny("zzabyycdxx", ['a','c']) returns 6
1228             * lastIndexOfAny("zzabyycdxx", ['c','a']) returns 6
1229             * lastIndexOfAny("zzabyycdxx", ['m','n']) returns -1
1230             * </code>
1231             * </pre>
1232             * </p>
1233             *
1234             * @param  s the string to search (optionally <code>null</code>)
1235             * @param  chars the characters to search for (optionally <code>null</code>)
1236             * @return the index within the string of the last occurrence of any
1237             *         character from the array, or <code>-1</code> if none of the
1238             *         characters occur
1239             */
1240            public static int lastIndexOfAny(String s, char[] chars) {
1241                    if (s == null) {
1242                            return -1;
1243                    }
1244    
1245                    return lastIndexOfAny(s, chars, 0, s.length() - 1);
1246            }
1247    
1248            /**
1249             * Returns the index within the string of the last occurrence of any
1250             * character from the array, starting the search at the specified index
1251             * within the string.
1252             *
1253             * <p>
1254             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1255             * or empty array returns <code>-1</code>.
1256             * </p>
1257             *
1258             * <p>
1259             * Examples:
1260             * </p>
1261             *
1262             * <p>
1263             * <pre>
1264             * <code>
1265             * lastIndexOfAny(null, *, *) returns -1
1266             * lastIndexOfAny(*, null, *) returns -1
1267             * lastIndexOfAny(*, [], *) returns -1
1268             * lastIndexOfAny("zzabyycdxx", ['a','c'], 5) returns 2
1269             * lastIndexOfAny("zzabyycdxx", ['m','n'], *) returns -1
1270             * </code>
1271             * </pre>
1272             * </p>
1273             *
1274             * @param  s the string to search (optionally <code>null</code>)
1275             * @param  chars the characters to search for (optionally <code>null</code>)
1276             * @param  toIndex the end index within the string
1277             * @return the index within the string of the last occurrence of any
1278             *         character from the array, starting the search at the specified
1279             *         index within the string, or <code>-1</code> if none of the
1280             *         characters occur
1281             */
1282            public static int lastIndexOfAny(String s, char[] chars, int toIndex) {
1283                    if (s == null) {
1284                            return -1;
1285                    }
1286    
1287                    return lastIndexOfAny(s, chars, 0, toIndex);
1288            }
1289    
1290            /**
1291             * Returns the index within the string of the last occurrence of any
1292             * character from the array, up to and including the specified end index
1293             * within the string, starting the search at the specified start index
1294             * within the string.
1295             *
1296             * <p>
1297             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1298             * or empty array returns <code>-1</code>.
1299             * </p>
1300             *
1301             * <p>
1302             * Examples:
1303             * </p>
1304             *
1305             * <p>
1306             * <pre>
1307             * <code>
1308             * lastIndexOfAny(null</code>, *, *, *) returns -1
1309             * lastIndexOfAny(*, null</code>, *, *) returns -1
1310             * lastIndexOfAny(*, [], *, *) returns -1
1311             * lastIndexOfAny("zzabyycdxx", ['a','c'], 5, 7) returns 6
1312             * lastIndexOfAny("zzabyycdxx", ['m','n'], *, *) returns -1
1313             * </code>
1314             * </pre>
1315             * </p>
1316             *
1317             * @param  s the string to search (optionally <code>null</code>)
1318             * @param  chars the characters to search for (optionally <code>null</code>)
1319             * @param  fromIndex the start index within the string
1320             * @param  toIndex the end index within the string
1321             * @return the index within the string of the last occurrence of any
1322             *         character from the array, up to and including the specified end
1323             *         index within the string, starting the search at the specified
1324             *         start index within the string, or <code>-1</code> if none of the
1325             *         characters occur
1326             */
1327            public static int lastIndexOfAny(
1328                    String s, char[] chars, int fromIndex, int toIndex) {
1329    
1330                    if ((s == null) || (toIndex < fromIndex)) {
1331                            return -1;
1332                    }
1333    
1334                    if (ArrayUtil.isEmpty(chars)) {
1335                            return -1;
1336                    }
1337    
1338                    if (fromIndex >= s.length()) {
1339                            return -1;
1340                    }
1341    
1342                    if (fromIndex < 0) {
1343                            fromIndex = 0;
1344                    }
1345    
1346                    if (toIndex >= s.length()) {
1347                            toIndex = s.length() - 1;
1348                    }
1349    
1350                    for (int i = toIndex; i >= fromIndex; i--) {
1351                            char c = s.charAt(i);
1352    
1353                            for (int j = 0; j < chars.length; j++) {
1354                                    if (c == chars[j]) {
1355                                            return i;
1356                                    }
1357                            }
1358                    }
1359    
1360                    return -1;
1361            }
1362    
1363            /**
1364             * Returns the index within the string of the last occurrence of any string
1365             * from the array.
1366             *
1367             * <p>
1368             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1369             * or empty array returns <code>-1</code>, but an array containing
1370             * <code>""</code> returns <code>0</code> if the string is not
1371             * <code>null</code>.
1372             * </p>
1373             *
1374             * <p>
1375             * Examples:
1376             * </p>
1377             *
1378             * <p>
1379             * <pre>
1380             * <code>
1381             * lastIndexOfAny(null</code>, *) returns -1
1382             * lastIndexOfAny(*, null</code>) returns -1
1383             * lastIndexOfAny(*, []) returns -1
1384             * lastIndexOfAny(*, [null</code>]) returns -1
1385             * lastIndexOfAny("zzabyycdxx", ["ab","cd"]) returns 6
1386             * lastIndexOfAny("zzabyycdxx", ["cd","ab"]) returns 6
1387             * lastIndexOfAny("zzabyycdxx", ["mn","op"]) returns -1
1388             * lastIndexOfAny("zzabyycdxx", ["mn",""]) returns 10
1389             * </code>
1390             * </pre>
1391             * </p>
1392             *
1393             * @param  s the string to search (optionally <code>null</code>)
1394             * @param  texts the strings to search for (optionally <code>null</code>)
1395             * @return the index within the string of the last occurrence of any string
1396             *         from the array, <code>0</code> if the search array contains
1397             *         <code>""</code>, or <code>-1</code> if none of the strings occur
1398             */
1399            public static int lastIndexOfAny(String s, String[] texts) {
1400                    if (s == null) {
1401                            return -1;
1402                    }
1403    
1404                    return lastIndexOfAny(s, texts, 0, s.length() - 1);
1405            }
1406    
1407            /**
1408             * Returns the index within the string of the last occurrence of any string
1409             * from the array, starting the search at the specified index within the
1410             * string.
1411             *
1412             * <p>
1413             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1414             * or empty array returns <code>-1</code>, but an array containing
1415             * <code>""</code> returns the specified start index if the string is not
1416             * <code>null</code>.
1417             * </p>
1418             *
1419             * <p>
1420             * Examples:
1421             * </p>
1422             *
1423             * <p>
1424             * <pre>
1425             * <code>
1426             * lastIndexOfAny(null, *, *) returns -1
1427             * lastIndexOfAny(*, null, *) returns -1
1428             * lastIndexOfAny(*, [], *) returns -1
1429             * lastIndexOfAny(*, [null], *) returns -1
1430             * lastIndexOfAny("zzabyycdxx", ["ab","cd"], 5) returns 2
1431             * lastIndexOfAny("zzabyycdxx", ["cd","ab"], 5) returns 2
1432             * lastIndexOfAny("zzabyycdxx", ["mn","op"], *) returns -1
1433             * lastIndexOfAny("zzabyycdxx", ["mn",""], 5) returns 5
1434             * </code>
1435             * </pre>
1436             * </p>
1437             *
1438             * @param  s the string to search (optionally <code>null</code>)
1439             * @param  texts the strings to search for (optionally <code>null</code>)
1440             * @param  toIndex the end index within the string
1441             * @return the index within the string of the last occurrence of any string
1442             *         from the array, starting the search at the specified index within
1443             *         the string, the start index if the search array contains
1444             *         <code>""</code>, or <code>-1</code> if none of the strings occur
1445             */
1446            public static int lastIndexOfAny(String s, String[] texts, int toIndex) {
1447                    if (s == null) {
1448                            return -1;
1449                    }
1450    
1451                    return lastIndexOfAny(s, texts, 0, toIndex);
1452            }
1453    
1454            /**
1455             * Returns the index within the string of the last occurrence of any string
1456             * from the array, up to and including the specified end index within the
1457             * string, starting the search at the specified start index within the
1458             * string.
1459             *
1460             * <p>
1461             * A <code>null</code> string returns <code>-1</code>. A <code>null</code>
1462             * or empty array returns <code>-1</code>, but an array containing
1463             * <code>""</code> returns the specified end index if the string is not
1464             * <code>null</code>.
1465             * </p>
1466             *
1467             * <p>
1468             * Examples:
1469             * </p>
1470             *
1471             * <p>
1472             * <pre>
1473             * <code>
1474             * lastIndexOfAny(null, *, *, *) returns -1
1475             * lastIndexOfAny(*, null, *, *) returns -1
1476             * lastIndexOfAny(*, [], *, *) returns -1
1477             * lastIndexOfAny(*, [null], *, *) returns -1
1478             * lastIndexOfAny("zzabyycdxx", ["ab","cd"], 2, 5) returns 2
1479             * lastIndexOfAny("zzabyycdxx", ["mn","op"], *, *) returns -1
1480             * lastIndexOfAny("zzabyycdxx", ["mn",""], 2, 5) returns 5
1481             * </code>
1482             * </pre>
1483             * </p>
1484             *
1485             * @param  s the string to search (optionally <code>null</code>)
1486             * @param  texts the strings to search for (optionally <code>null</code>)
1487             * @param  fromIndex the start index within the string
1488             * @param  toIndex the end index within the string
1489             * @return the index within the string of the last occurrence of any string
1490             *         from the array, up to and including the specified end index
1491             *         within the string, starting the search at the specified start
1492             *         index within the string, the end index if the search array
1493             *         contains <code>""</code>, or <code>-1</code> if none of the
1494             *         strings occur
1495             */
1496            public static int lastIndexOfAny(
1497                    String s, String[] texts, int fromIndex, int toIndex) {
1498    
1499                    if ((s == null) || (toIndex < fromIndex)) {
1500                            return -1;
1501                    }
1502    
1503                    if (ArrayUtil.isEmpty(texts)) {
1504                            return -1;
1505                    }
1506    
1507                    if (fromIndex >= s.length()) {
1508                            return -1;
1509                    }
1510    
1511                    if (fromIndex < 0) {
1512                            fromIndex = 0;
1513                    }
1514    
1515                    if (toIndex >= s.length()) {
1516                            toIndex = s.length() - 1;
1517                    }
1518    
1519                    for (int i = toIndex; i >= fromIndex; i--) {
1520                            for (int j = 0; j < texts.length; j++) {
1521                                    if (texts[j] == null) {
1522                                            continue;
1523                                    }
1524    
1525                                    if ((i + texts[j].length() <= toIndex + 1) &&
1526                                            s.startsWith(texts[j], i)) {
1527    
1528                                            return i;
1529                                    }
1530                            }
1531                    }
1532    
1533                    return -1;
1534            }
1535    
1536            /**
1537             * Converts all of the characters in the string to lower case.
1538             *
1539             * @param  s the string to convert
1540             * @return the string, converted to lower case, or <code>null</code> if the
1541             *         string is <code>null</code>
1542             * @see    String#toLowerCase()
1543             */
1544            public static String lowerCase(String s) {
1545                    return toLowerCase(s);
1546            }
1547    
1548            /**
1549             * Converts all of the characters in the arbitrary number of strings to
1550             * lower case.
1551             *
1552             * @param array the array or sequence of string arguments
1553             */
1554            public static void lowerCase(String... array) {
1555                    if (array != null) {
1556                            for (int i = 0; i < array.length; i++) {
1557                                    array[i] = toLowerCase(array[i]);
1558                            }
1559                    }
1560            }
1561    
1562            /**
1563             * Converts the first character of the string to lower case.
1564             *
1565             * @param  s the string whose first character is to be converted
1566             * @return the string, with its first character converted to lower-case
1567             */
1568            public static String lowerCaseFirstLetter(String s) {
1569                    char[] chars = s.toCharArray();
1570    
1571                    if ((chars[0] >= 65) && (chars[0] <= 90)) {
1572                            chars[0] = (char)(chars[0] + 32);
1573                    }
1574    
1575                    return new String(chars);
1576            }
1577    
1578            /**
1579             * Returns <code>true</code> if the specified pattern occurs at any position
1580             * in the string.
1581             *
1582             * @param  s the string
1583             * @param  pattern the pattern to search for in the string
1584             * @return <code>true</code> if the specified pattern occurs at any position
1585             *         in the string
1586             */
1587            public static boolean matches(String s, String pattern) {
1588                    String[] array = pattern.split("\\*");
1589    
1590                    for (String element : array) {
1591                            int pos = s.indexOf(element);
1592    
1593                            if (pos == -1) {
1594                                    return false;
1595                            }
1596    
1597                            s = s.substring(pos + element.length());
1598                    }
1599    
1600                    return true;
1601            }
1602    
1603            /**
1604             * Returns <code>true</code> if the specified pattern occurs at any position
1605             * in the string, ignoring case.
1606             *
1607             * @param  s the string
1608             * @param  pattern the pattern to search for in the string
1609             * @return <code>true</code> if the specified pattern occurs at any position
1610             *         in the string
1611             */
1612            public static boolean matchesIgnoreCase(String s, String pattern) {
1613                    return matches(lowerCase(s), lowerCase(pattern));
1614            }
1615    
1616            /**
1617             * Merges the elements of the boolean array into a string representing a
1618             * comma delimited list of its values.
1619             *
1620             * @param  array the boolean values to merge
1621             * @return a string representing a comma delimited list of the values of the
1622             *         boolean array, an empty string if the array is empty, or
1623             *         <code>null</code> if the array is <code>null</code>
1624             */
1625            public static String merge(boolean[] array) {
1626                    return merge(array, StringPool.COMMA);
1627            }
1628    
1629            /**
1630             * Merges the elements of the boolean array into a string representing a
1631             * delimited list of its values.
1632             *
1633             * @param  array the boolean values to merge
1634             * @param  delimiter the delimiter
1635             * @return a string representing a comma delimited list of the values of the
1636             *         boolean array, an empty string if the array is empty, or
1637             *         <code>null</code> if the array is <code>null</code>
1638             */
1639            public static String merge(boolean[] array, String delimiter) {
1640                    if (array == null) {
1641                            return null;
1642                    }
1643    
1644                    if (array.length == 0) {
1645                            return StringPool.BLANK;
1646                    }
1647    
1648                    StringBundler sb = new StringBundler(2 * array.length - 1);
1649    
1650                    for (int i = 0; i < array.length; i++) {
1651                            if (i != 0) {
1652                                    sb.append(delimiter);
1653                            }
1654    
1655                            sb.append(String.valueOf(array[i]));
1656                    }
1657    
1658                    return sb.toString();
1659            }
1660    
1661            /**
1662             * Merges the elements of the character array into a string representing a
1663             * comma delimited list of its values.
1664             *
1665             * @param  array the characters to merge
1666             * @return a string representing a comma delimited list of the values of the
1667             *         character array, an empty string if the array is empty, or
1668             *         <code>null</code> if the array is <code>null</code>
1669             */
1670            public static String merge(char[] array) {
1671                    return merge(array, StringPool.COMMA);
1672            }
1673    
1674            /**
1675             * Merges the elements of the character array into a string representing a
1676             * delimited list of its values.
1677             *
1678             * @param  array the characters to merge
1679             * @param  delimiter the delimiter
1680             * @return a string representing a delimited list of the values of the
1681             *         character array, an empty string if the array is empty, or
1682             *         <code>null</code> if the array is <code>null</code>
1683             */
1684            public static String merge(char[] array, String delimiter) {
1685                    if (array == null) {
1686                            return null;
1687                    }
1688    
1689                    if (array.length == 0) {
1690                            return StringPool.BLANK;
1691                    }
1692    
1693                    StringBundler sb = new StringBundler(2 * array.length - 1);
1694    
1695                    for (int i = 0; i < array.length; i++) {
1696                            if (i != 0) {
1697                                    sb.append(delimiter);
1698                            }
1699    
1700                            sb.append(String.valueOf(array[i]));
1701                    }
1702    
1703                    return sb.toString();
1704            }
1705    
1706            /**
1707             * Merges the elements of the collection by returning a string representing
1708             * a comma delimited list of its values.
1709             *
1710             * @param  col the collection of objects
1711             * @return the merged collection elements, or <code>null</code> if the
1712             *         collection is <code>null</code>
1713             */
1714            public static String merge(Collection<?> col) {
1715                    return merge(col, StringPool.COMMA);
1716            }
1717    
1718            /**
1719             * Merges the elements of the collection by returning a string representing
1720             * a delimited list of its values.
1721             *
1722             * @param  col the collection of objects
1723             * @param  delimiter the string whose last index in the string marks where
1724             *         to begin the substring
1725             * @return the merged collection elements, or <code>null</code> if the
1726             *         collection is <code>null</code>
1727             */
1728            public static String merge(Collection<?> col, String delimiter) {
1729                    if (col == null) {
1730                            return null;
1731                    }
1732    
1733                    return merge(col.toArray(new Object[col.size()]), delimiter);
1734            }
1735    
1736            /**
1737             * Merges the elements of an array of double-precision decimal numbers by
1738             * returning a string representing a comma delimited list of its values.
1739             *
1740             * @param  array the doubles to merge
1741             * @return a string representing a comma delimited list of the values of the
1742             *         array of double-precision decimal numbers, an empty string if the
1743             *         array is empty, or <code>null</code> if the array is
1744             *         <code>null</code>
1745             */
1746            public static String merge(double[] array) {
1747                    return merge(array, StringPool.COMMA);
1748            }
1749    
1750            /**
1751             * Merges the elements of an array of double-precision decimal numbers by
1752             * returning a string representing a delimited list of its values.
1753             *
1754             * @param  array the doubles to merge
1755             * @param  delimiter the delimiter
1756             * @return a string representing a delimited list of the values of the array
1757             *         of double-precision decimal numbers, an empty string if the array
1758             *         is empty, or <code>null</code> if the array is <code>null</code>
1759             */
1760            public static String merge(double[] array, String delimiter) {
1761                    if (array == null) {
1762                            return null;
1763                    }
1764    
1765                    if (array.length == 0) {
1766                            return StringPool.BLANK;
1767                    }
1768    
1769                    StringBundler sb = new StringBundler(2 * array.length - 1);
1770    
1771                    for (int i = 0; i < array.length; i++) {
1772                            if (i != 0) {
1773                                    sb.append(delimiter);
1774                            }
1775    
1776                            sb.append(String.valueOf(array[i]));
1777                    }
1778    
1779                    return sb.toString();
1780            }
1781    
1782            /**
1783             * Merges the elements of an array of decimal numbers into a string
1784             * representing a comma delimited list of its values.
1785             *
1786             * @param  array the floats to merge
1787             * @return a string representing a comma delimited list of the values of the
1788             *         array of decimal numbers, an empty string if the array is empty,
1789             *         or <code>null</code> if the array is <code>null</code>
1790             */
1791            public static String merge(float[] array) {
1792                    return merge(array, StringPool.COMMA);
1793            }
1794    
1795            /**
1796             * Merges the elements of an array of decimal numbers into a string
1797             * representing a delimited list of its values.
1798             *
1799             * @param  array the floats to merge
1800             * @param  delimiter the delimiter
1801             * @return a string representing a delimited list of the values of the array
1802             *         of decimal numbers, an empty string if the array is empty, or
1803             *         <code>null</code> if the array is <code>null</code>
1804             */
1805            public static String merge(float[] array, String delimiter) {
1806                    if (array == null) {
1807                            return null;
1808                    }
1809    
1810                    if (array.length == 0) {
1811                            return StringPool.BLANK;
1812                    }
1813    
1814                    StringBundler sb = new StringBundler(2 * array.length - 1);
1815    
1816                    for (int i = 0; i < array.length; i++) {
1817                            if (i != 0) {
1818                                    sb.append(delimiter);
1819                            }
1820    
1821                            sb.append(String.valueOf(array[i]));
1822                    }
1823    
1824                    return sb.toString();
1825            }
1826    
1827            /**
1828             * Merges the elements of an array of integers into a string representing a
1829             * comma delimited list of its values.
1830             *
1831             * @param  array the integers to merge
1832             * @return a string representing a comma delimited list of the values of the
1833             *         array of integers, an empty string if the array is empty, or
1834             *         <code>null</code> if the array is <code>null</code>
1835             */
1836            public static String merge(int[] array) {
1837                    return merge(array, StringPool.COMMA);
1838            }
1839    
1840            /**
1841             * Merges the elements of an array of integers into a string representing a
1842             * delimited list of its values.
1843             *
1844             * @param  array the integers to merge
1845             * @param  delimiter the delimiter
1846             * @return a string representing a delimited list of the values of the array
1847             *         of integers, an empty string if the array is empty, or
1848             *         <code>null</code> if the array is <code>null</code>
1849             */
1850            public static String merge(int[] array, String delimiter) {
1851                    if (array == null) {
1852                            return null;
1853                    }
1854    
1855                    if (array.length == 0) {
1856                            return StringPool.BLANK;
1857                    }
1858    
1859                    StringBundler sb = new StringBundler(2 * array.length - 1);
1860    
1861                    for (int i = 0; i < array.length; i++) {
1862                            if (i != 0) {
1863                                    sb.append(delimiter);
1864                            }
1865    
1866                            sb.append(String.valueOf(array[i]));
1867                    }
1868    
1869                    return sb.toString();
1870            }
1871    
1872            /**
1873             * Merges the elements of an array of long integers by returning a string
1874             * representing a comma delimited list of its values.
1875             *
1876             * @param  array the long integers to merge
1877             * @return a string representing a comma delimited list of the values of the
1878             *         array of long integers, an empty string if the array is empty, or
1879             *         <code>null</code> if the array is <code>null</code>
1880             */
1881            public static String merge(long[] array) {
1882                    return merge(array, StringPool.COMMA);
1883            }
1884    
1885            /**
1886             * Merges the elements of an array of long integers by returning a string
1887             * representing a delimited list of its values.
1888             *
1889             * @param  array the long integers to merge
1890             * @param  delimiter the delimiter
1891             * @return a string representing a delimited list of the values of the array
1892             *         of long integers, an empty string if the array is empty, or
1893             *         <code>null</code> if the array is <code>null</code>
1894             */
1895            public static String merge(long[] array, String delimiter) {
1896                    if (array == null) {
1897                            return null;
1898                    }
1899    
1900                    if (array.length == 0) {
1901                            return StringPool.BLANK;
1902                    }
1903    
1904                    StringBundler sb = new StringBundler(2 * array.length - 1);
1905    
1906                    for (int i = 0; i < array.length; i++) {
1907                            if (i != 0) {
1908                                    sb.append(delimiter);
1909                            }
1910    
1911                            sb.append(String.valueOf(array[i]));
1912                    }
1913    
1914                    return sb.toString();
1915            }
1916    
1917            /**
1918             * Merges the elements of an array of objects into a string representing a
1919             * comma delimited list of the objects.
1920             *
1921             * @param  array the objects to merge
1922             * @return a string representing a comma delimited list of the objects, an
1923             *         empty string if the array is empty, or <code>null</code> if the
1924             *         array is <code>null</code>
1925             */
1926            public static String merge(Object[] array) {
1927                    return merge(array, StringPool.COMMA);
1928            }
1929    
1930            /**
1931             * Merges the elements of an array of objects into a string representing a
1932             * delimited list of the objects.
1933             *
1934             * @param  array the objects to merge
1935             * @param  delimiter the delimiter
1936             * @return a string representing a delimited list of the objects, an empty
1937             *         string if the array is empty, or <code>null</code> if the array
1938             *         is <code>null</code>
1939             */
1940            public static String merge(Object[] array, String delimiter) {
1941                    if (array == null) {
1942                            return null;
1943                    }
1944    
1945                    if (array.length == 0) {
1946                            return StringPool.BLANK;
1947                    }
1948    
1949                    StringBundler sb = new StringBundler(2 * array.length - 1);
1950    
1951                    for (int i = 0; i < array.length; i++) {
1952                            if (i != 0) {
1953                                    sb.append(delimiter);
1954                            }
1955    
1956                            sb.append(String.valueOf(array[i]).trim());
1957                    }
1958    
1959                    return sb.toString();
1960            }
1961    
1962            /**
1963             * Merges the elements of an array of short integers by returning a string
1964             * representing a comma delimited list of its values.
1965             *
1966             * @param  array the short integers to merge
1967             * @return a string representing a comma delimited list of the values of the
1968             *         array of short integers, an empty string if the array is empty,
1969             *         or <code>null</code> if the array is <code>null</code>
1970             */
1971            public static String merge(short[] array) {
1972                    return merge(array, StringPool.COMMA);
1973            }
1974    
1975            /**
1976             * Merges the elements of an array of short integers by returning a string
1977             * representing a delimited list of its values.
1978             *
1979             * @param  array the short integers to merge
1980             * @param  delimiter the delimiter
1981             * @return a string representing a delimited list of the values of the array
1982             *         of short integers, an empty string if the array is empty, or
1983             *         <code>null</code> if the array is <code>null</code>
1984             */
1985            public static String merge(short[] array, String delimiter) {
1986                    if (array == null) {
1987                            return null;
1988                    }
1989    
1990                    if (array.length == 0) {
1991                            return StringPool.BLANK;
1992                    }
1993    
1994                    StringBundler sb = new StringBundler(2 * array.length - 1);
1995    
1996                    for (int i = 0; i < array.length; i++) {
1997                            if (i != 0) {
1998                                    sb.append(delimiter);
1999                            }
2000    
2001                            sb.append(String.valueOf(array[i]));
2002                    }
2003    
2004                    return sb.toString();
2005            }
2006    
2007            /**
2008             * Returns the string enclosed by apostrophes.
2009             *
2010             * <p>
2011             * Example:
2012             * </p>
2013             *
2014             * <p>
2015             * <pre>
2016             * <code>
2017             * quote("Hello, World!") returns "'Hello, World!'"
2018             * </code>
2019             * </pre>
2020             * </p>
2021             *
2022             * @param  s the string to enclose in apostrophes
2023             * @return the string enclosed by apostrophes, or <code>null</code> if the
2024             *         string is <code>null</code>
2025             */
2026            public static String quote(String s) {
2027                    return quote(s, CharPool.APOSTROPHE);
2028            }
2029    
2030            /**
2031             * Returns the string enclosed by the quote character.
2032             *
2033             * <p>
2034             * Example:
2035             * </p>
2036             *
2037             * <p>
2038             * <pre>
2039             * <code>
2040             * quote("PATH", '%') returns "%PATH%"
2041             * </code>
2042             * </pre>
2043             * </p>
2044             *
2045             * @param  s the string to enclose in quotes
2046             * @param  quote the character to insert to insert to the beginning of and
2047             *         append to the end of the string
2048             * @return the string enclosed in the quote characters, or <code>null</code>
2049             *         if the string is <code>null</code>
2050             */
2051            public static String quote(String s, char quote) {
2052                    if (s == null) {
2053                            return null;
2054                    }
2055    
2056                    return quote(s, String.valueOf(quote));
2057            }
2058    
2059            /**
2060             * Returns the string enclosed by the quote strings.
2061             *
2062             * <p>
2063             * Example:
2064             * </p>
2065             *
2066             * <p>
2067             * <pre>
2068             * <code>
2069             * quote("WARNING", "!!!") returns "!!!WARNING!!!"
2070             * </code>
2071             * </pre>
2072             * </p>
2073             *
2074             * @param  s the string to enclose in quotes
2075             * @param  quote the quote string to insert to insert to the beginning of
2076             *         and append to the end of the string
2077             * @return the string enclosed in the quote strings, or <code>null</code> if
2078             *         the string is <code>null</code>
2079             */
2080            public static String quote(String s, String quote) {
2081                    if (s == null) {
2082                            return null;
2083                    }
2084    
2085                    return quote.concat(s).concat(quote);
2086            }
2087    
2088            /**
2089             * Returns a randomized string of four lower case, alphabetic characters.
2090             *
2091             * @return a randomized string of four lower case, alphabetic characters
2092             */
2093            public static String randomId() {
2094                    Random random = new Random();
2095    
2096                    char[] chars = new char[4];
2097    
2098                    for (int i = 0; i < 4; i++) {
2099                            chars[i] = (char)(CharPool.LOWER_CASE_A + random.nextInt(26));
2100                    }
2101    
2102                    return new String(chars);
2103            }
2104    
2105            /**
2106             * Pseudorandomly permutes the characters of the string.
2107             *
2108             * @param  s the string whose characters are to be randomized
2109             * @return a string of the same length as the string whose characters
2110             *         represent a pseudorandom permutation of the characters of the
2111             *         string
2112             */
2113            public static String randomize(String s) {
2114                    return RandomUtil.shuffle(s);
2115            }
2116    
2117            /**
2118             * Returns a randomized string of eight characters consisting of lower case
2119             * letters, upper case letters, and single-digit whole numbers.
2120             *
2121             * @return a randomized string of eight characters consisting of lower case
2122             *         letters, upper case letters, and single-digit whole numbers
2123             */
2124            public static String randomString() {
2125                    return randomString(8);
2126            }
2127    
2128            /**
2129             * Returns a randomized string of the specified length consisting of lower
2130             * case letters, upper case letters, and single-digit whole numbers.
2131             *
2132             * @param  length the character length of the randomized string
2133             * @return a randomized string of the specified length consisting of lower
2134             *         case letters, upper case letters, and single-digit whole numbers
2135             */
2136            public static String randomString(int length) {
2137                    Random random = new Random();
2138    
2139                    char[] chars = new char[length];
2140    
2141                    for (int i = 0; i < length; i++) {
2142                            int index = random.nextInt(_RANDOM_STRING_CHAR_TABLE.length);
2143    
2144                            chars[i] = _RANDOM_STRING_CHAR_TABLE[index];
2145                    }
2146    
2147                    return new String(chars);
2148            }
2149    
2150            public static String read(ClassLoader classLoader, String name)
2151                    throws IOException {
2152    
2153                    return read(classLoader, name, false);
2154            }
2155    
2156            public static String read(ClassLoader classLoader, String name, boolean all)
2157                    throws IOException {
2158    
2159                    if (all) {
2160                            StringBundler sb = new StringBundler();
2161    
2162                            Enumeration<URL> enu = classLoader.getResources(name);
2163    
2164                            while (enu.hasMoreElements()) {
2165                                    URL url = enu.nextElement();
2166    
2167                                    InputStream is = url.openStream();
2168    
2169                                    if (is == null) {
2170                                            throw new IOException(
2171                                                    "Unable to open resource at " + url.toString());
2172                                    }
2173    
2174                                    try {
2175                                            String s = read(is);
2176    
2177                                            if (s != null) {
2178                                                    sb.append(s);
2179                                                    sb.append(StringPool.NEW_LINE);
2180                                            }
2181                                    }
2182                                    finally {
2183                                            StreamUtil.cleanUp(is);
2184                                    }
2185                            }
2186    
2187                            return sb.toString().trim();
2188                    }
2189    
2190                    InputStream is = classLoader.getResourceAsStream(name);
2191    
2192                    if (is == null) {
2193                            throw new IOException(
2194                                    "Unable to open resource in class loader " + name);
2195                    }
2196    
2197                    try {
2198                            String s = read(is);
2199    
2200                            return s;
2201                    }
2202                    finally {
2203                            StreamUtil.cleanUp(is);
2204                    }
2205            }
2206    
2207            public static String read(InputStream is) throws IOException {
2208                    StringBundler sb = new StringBundler();
2209    
2210                    try (UnsyncBufferedReader unsyncBufferedReader =
2211                                    new UnsyncBufferedReader(new InputStreamReader(is))) {
2212    
2213                            String line = null;
2214    
2215                            while ((line = unsyncBufferedReader.readLine()) != null) {
2216                                    sb.append(line);
2217                                    sb.append(CharPool.NEW_LINE);
2218                            }
2219                    }
2220    
2221                    return sb.toString().trim();
2222            }
2223    
2224            public static void readLines(InputStream is, Collection<String> lines)
2225                    throws IOException {
2226    
2227                    try (UnsyncBufferedReader unsyncBufferedReader =
2228                                    new UnsyncBufferedReader(new InputStreamReader(is))) {
2229    
2230                            String line = null;
2231    
2232                            while ((line = unsyncBufferedReader.readLine()) != null) {
2233                                    lines.add(line);
2234                            }
2235                    }
2236            }
2237    
2238            /**
2239             * @deprecated As of 7.0.0, replaced by {@link #removeFromList(String,
2240             *             String)}
2241             */
2242            @Deprecated
2243            public static String remove(String s, String element) {
2244                    return removeFromList(s, element, StringPool.COMMA);
2245            }
2246    
2247            /**
2248             * @deprecated As of 7.0.0, replaced by {@link #removeFromList(String,
2249             *             String, String)}
2250             */
2251            @Deprecated
2252            public static String remove(String s, String element, String delimiter) {
2253                    return removeFromList(s, element, delimiter);
2254            }
2255    
2256            /**
2257             * Removes the <code>remove</code> string from string <code>s</code> that
2258             * represents a list of comma delimited strings.
2259             *
2260             * <p>
2261             * The resulting string ends with a comma even if the original string does
2262             * not.
2263             * </p>
2264             *
2265             * <p>
2266             * Examples:
2267             * </p>
2268             *
2269             * <p>
2270             * <pre>
2271             * <code>
2272             * remove("red,blue,green,yellow", "blue") returns "red,green,yellow,"
2273             * remove("blue", "blue") returns ""
2274             * remove("blue,", "blue") returns ""
2275             * </code>
2276             * </pre>
2277             * </p>
2278             *
2279             * @param  s the string representing the list of comma delimited strings
2280             * @param  element the string to remove
2281             * @return a string representing the list of comma delimited strings with
2282             *         the <code>remove</code> string removed, or <code>null</code> if
2283             *         the original string, the string to remove, or the delimiter is
2284             *         <code>null</code>
2285             */
2286            public static String removeFromList(String s, String element) {
2287                    return removeFromList(s, element, StringPool.COMMA);
2288            }
2289    
2290            /**
2291             * Removes the <code>remove</code> string from string <code>s</code> that
2292             * represents a list of delimited strings.
2293             *
2294             * <p>
2295             * The resulting string ends with the delimiter even if the original string
2296             * does not.
2297             * </p>
2298             *
2299             * <p>
2300             * Examples:
2301             * </p>
2302             *
2303             * <p>
2304             * <pre>
2305             * <code>
2306             * remove("red;blue;green;yellow", "blue", ";") returns "red;green;yellow;"
2307             * remove("blue", "blue", ";") returns ""
2308             * remove("blue;", "blue", ";") returns ""
2309             * </code>
2310             * </pre>
2311             * </p>
2312             *
2313             * @param  s the string representing the list of delimited strings
2314             * @param  element the string to remove
2315             * @param  delimiter the delimiter
2316             * @return a string representing the list of delimited strings with the
2317             *         <code>remove</code> string removed, or <code>null</code> if the
2318             *         original string, the string to remove, or the delimiter is
2319             *         <code>null</code>
2320             */
2321            public static String removeFromList(
2322                    String s, String element, String delimiter) {
2323    
2324                    if ((s == null) || (element == null) || (delimiter == null)) {
2325                            return null;
2326                    }
2327    
2328                    if (Validator.isNotNull(s) && !s.endsWith(delimiter)) {
2329                            s += delimiter;
2330                    }
2331    
2332                    String drd = delimiter.concat(element).concat(delimiter);
2333    
2334                    String rd = element.concat(delimiter);
2335    
2336                    while (contains(s, element, delimiter)) {
2337                            int pos = s.indexOf(drd);
2338    
2339                            if (pos == -1) {
2340                                    if (s.startsWith(rd)) {
2341                                            int x = element.length() + delimiter.length();
2342                                            int y = s.length();
2343    
2344                                            s = s.substring(x, y);
2345                                    }
2346                            }
2347                            else {
2348                                    int x = pos + element.length() + delimiter.length();
2349                                    int y = s.length();
2350    
2351                                    String temp = s.substring(0, pos);
2352    
2353                                    s = temp.concat(s.substring(x, y));
2354                            }
2355                    }
2356    
2357                    return s;
2358            }
2359    
2360            /**
2361             * Replaces all occurrences of the character with the new character.
2362             *
2363             * @param  s the original string
2364             * @param  oldSub the character to be searched for and replaced in the
2365             *         original string
2366             * @param  newSub the character with which to replace the
2367             *         <code>oldSub</code> character
2368             * @return a string representing the original string with all occurrences of
2369             *         the <code>oldSub</code> character replaced with the
2370             *         <code>newSub</code> character, or <code>null</code> if the
2371             *         original string is <code>null</code>
2372             */
2373            public static String replace(String s, char oldSub, char newSub) {
2374                    if (s == null) {
2375                            return null;
2376                    }
2377    
2378                    return s.replace(oldSub, newSub);
2379            }
2380    
2381            /**
2382             * Replaces all occurrences of the character with the new string.
2383             *
2384             * @param  s the original string
2385             * @param  oldSub the character to be searched for and replaced in the
2386             *         original string
2387             * @param  newSub the string with which to replace the <code>oldSub</code>
2388             *         character
2389             * @return a string representing the original string with all occurrences of
2390             *         the <code>oldSub</code> character replaced with the string
2391             *         <code>newSub</code>, or <code>null</code> if the original string
2392             *         is <code>null</code>
2393             */
2394            public static String replace(String s, char oldSub, String newSub) {
2395                    if ((s == null) || (newSub == null)) {
2396                            return null;
2397                    }
2398    
2399                    // The number 5 is arbitrary and is used as extra padding to reduce
2400                    // buffer expansion
2401    
2402                    StringBundler sb = new StringBundler(s.length() + 5 * newSub.length());
2403    
2404                    char[] chars = s.toCharArray();
2405    
2406                    for (char c : chars) {
2407                            if (c == oldSub) {
2408                                    sb.append(newSub);
2409                            }
2410                            else {
2411                                    sb.append(c);
2412                            }
2413                    }
2414    
2415                    return sb.toString();
2416            }
2417    
2418            /**
2419             * Replaces all occurrences of the string with the new string.
2420             *
2421             * @param  s the original string
2422             * @param  oldSub the string to be searched for and replaced in the original
2423             *         string
2424             * @param  newSub the string with which to replace the <code>oldSub</code>
2425             *         string
2426             * @return a string representing the original string with all occurrences of
2427             *         the <code>oldSub</code> string replaced with the string
2428             *         <code>newSub</code>, or <code>null</code> if the original string
2429             *         is <code>null</code>
2430             */
2431            public static String replace(String s, String oldSub, String newSub) {
2432                    return replace(s, oldSub, newSub, 0);
2433            }
2434    
2435            /**
2436             * Replaces all occurrences of the string with the new string, starting from
2437             * the specified index.
2438             *
2439             * @param  s the original string
2440             * @param  oldSub the string to be searched for and replaced in the original
2441             *         string
2442             * @param  newSub the string with which to replace the <code>oldSub</code>
2443             *         string
2444             * @param  fromIndex the index of the original string from which to begin
2445             *         searching
2446             * @return a string representing the original string with all occurrences of
2447             *         the <code>oldSub</code> string occurring after the specified
2448             *         index replaced with the string <code>newSub</code>, or
2449             *         <code>null</code> if the original string is <code>null</code>
2450             */
2451            public static String replace(
2452                    String s, String oldSub, String newSub, int fromIndex) {
2453    
2454                    if (s == null) {
2455                            return null;
2456                    }
2457    
2458                    if ((oldSub == null) || oldSub.equals(StringPool.BLANK)) {
2459                            return s;
2460                    }
2461    
2462                    if (newSub == null) {
2463                            newSub = StringPool.BLANK;
2464                    }
2465    
2466                    int y = s.indexOf(oldSub, fromIndex);
2467    
2468                    if (y >= 0) {
2469                            StringBundler sb = new StringBundler();
2470    
2471                            int length = oldSub.length();
2472                            int x = 0;
2473    
2474                            while (x <= y) {
2475                                    sb.append(s.substring(x, y));
2476                                    sb.append(newSub);
2477    
2478                                    x = y + length;
2479                                    y = s.indexOf(oldSub, x);
2480                            }
2481    
2482                            sb.append(s.substring(x));
2483    
2484                            return sb.toString();
2485                    }
2486                    else {
2487                            return s;
2488                    }
2489            }
2490    
2491            /**
2492             * Replaces all occurrences of the keywords found in the substring, defined
2493             * by the beginning and ending strings, with the new values.
2494             *
2495             * <p>
2496             * For example, with the following initialized variables:
2497             * </p>
2498             *
2499             * <p>
2500             * <pre>
2501             * <code>
2502             * String s = "http://www.example-url/${userId}";
2503             * String begin = "${";
2504             * String end = "}";
2505             * Map<String, String> values =  new HashMap&#60;String, String&#62;();
2506             * values.put("userId", "jbloggs");
2507             * </code>
2508             * </pre>
2509             * </p>
2510             *
2511             * <p>
2512             * <code>replace(s, begin, end, values)</code> returns
2513             * <code>"http://www.example-url/jbloggs"</code>
2514             * </p>
2515             *
2516             * @param  s the original string
2517             * @param  begin the string preceding the substring to be modified. This
2518             *         string is excluded from the result.
2519             * @param  end the string following the substring to be modified. This
2520             *         string is excluded from the result.
2521             * @param  values the key-value map values
2522             * @return a string representing the original string with all occurrences of
2523             *         the of the keywords found in the substring, replaced with the new
2524             *         values. <code>null</code> is returned if the original string, the
2525             *         beginning string, the ending string, or the key-map values are
2526             *         <code>null</code>.
2527             */
2528            public static String replace(
2529                    String s, String begin, String end, Map<String, String> values) {
2530    
2531                    StringBundler sb = replaceToStringBundler(s, begin, end, values);
2532    
2533                    return sb.toString();
2534            }
2535    
2536            /**
2537             * Replaces all occurrences of the elements of the string array with the
2538             * corresponding elements of the new string array.
2539             *
2540             * @param  s the original string
2541             * @param  oldSubs the strings to be searched for and replaced in the
2542             *         original string
2543             * @param  newSubs the strings with which to replace the
2544             *         <code>oldSubs</code> strings
2545             * @return a string representing the original string with all occurrences of
2546             *         the <code>oldSubs</code> strings replaced with the corresponding
2547             *         <code>newSubs</code> strings, or <code>null</code> if the
2548             *         original string, the <code>oldSubs</code> array, or the
2549             *         <code>newSubs</code> is <code>null</code>
2550             */
2551            public static String replace(String s, String[] oldSubs, String[] newSubs) {
2552                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
2553                            return null;
2554                    }
2555    
2556                    if (oldSubs.length != newSubs.length) {
2557                            return s;
2558                    }
2559    
2560                    for (int i = 0; i < oldSubs.length; i++) {
2561                            s = replace(s, oldSubs[i], newSubs[i]);
2562                    }
2563    
2564                    return s;
2565            }
2566    
2567            /**
2568             * Replaces all occurrences of the elements of the string array with the
2569             * corresponding elements of the new string array, optionally replacing only
2570             * substrings that are surrounded by word boundaries.
2571             *
2572             * <p>
2573             * Examples:
2574             * </p>
2575             *
2576             * <p>
2577             * <pre>
2578             * <code>
2579             * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGEYELLOW"
2580             * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorangeyellow"
2581             * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGE YELLOW"
2582             * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorange YELLOW"
2583             * replace("red orange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "RED ORANGE YELLOW"
2584             * replace("redorange.yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", * "YELLOW"}, true) returns "redorange.YELLOW"
2585             * </code>
2586             * </pre>
2587             * </p>
2588             *
2589             * @param  s the original string
2590             * @param  oldSubs the strings to be searched for and replaced in the
2591             *         original string
2592             * @param  newSubs the strings with which to replace the
2593             *         <code>oldSubs</code> strings
2594             * @param  exactMatch whether or not to replace only substrings of
2595             *         <code>s</code> that are surrounded by word boundaries
2596             * @return if <code>exactMatch</code> is <code>true</code>, a string
2597             *         representing the original string with all occurrences of the
2598             *         <code>oldSubs</code> strings that are surrounded by word
2599             *         boundaries replaced with the corresponding <code>newSubs</code>
2600             *         strings, or else a string representing the original string with
2601             *         all occurrences of the <code>oldSubs</code> strings replaced with
2602             *         the corresponding <code>newSubs</code> strings, or
2603             *         <code>null</code> if the original string, the
2604             *         <code>oldSubs</code> array, or the <code>newSubs</code> is
2605             *         <code>null</code>
2606             */
2607            public static String replace(
2608                    String s, String[] oldSubs, String[] newSubs, boolean exactMatch) {
2609    
2610                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
2611                            return null;
2612                    }
2613    
2614                    if (oldSubs.length != newSubs.length) {
2615                            return s;
2616                    }
2617    
2618                    if (!exactMatch) {
2619                            return replace(s, oldSubs, newSubs);
2620                    }
2621    
2622                    for (int i = 0; i < oldSubs.length; i++) {
2623                            s = s.replaceAll("\\b" + oldSubs[i] + "\\b", newSubs[i]);
2624                    }
2625    
2626                    return s;
2627            }
2628    
2629            /**
2630             * Replaces the first occurrence of the character with the new character.
2631             *
2632             * @param  s the original string
2633             * @param  oldSub the character whose first occurrence in the original
2634             *         string is to be searched for and replaced
2635             * @param  newSub the character with which to replace the first occurrence
2636             *         of the <code>oldSub</code> character
2637             * @return a string representing the original string except with the first
2638             *         occurrence of the character <code>oldSub</code> replaced with the
2639             *         character <code>newSub</code>
2640             */
2641            public static String replaceFirst(String s, char oldSub, char newSub) {
2642                    if (s == null) {
2643                            return null;
2644                    }
2645    
2646                    return replaceFirst(s, String.valueOf(oldSub), String.valueOf(newSub));
2647            }
2648    
2649            /**
2650             * Replaces the first occurrence of the character with the new string.
2651             *
2652             * @param  s the original string
2653             * @param  oldSub the character whose first occurrence in the original
2654             *         string is to be searched for and replaced
2655             * @param  newSub the string with which to replace the first occurrence of
2656             *         the <code>oldSub</code> character
2657             * @return a string representing the original string except with the first
2658             *         occurrence of the character <code>oldSub</code> replaced with the
2659             *         string <code>newSub</code>
2660             */
2661            public static String replaceFirst(String s, char oldSub, String newSub) {
2662                    if ((s == null) || (newSub == null)) {
2663                            return null;
2664                    }
2665    
2666                    return replaceFirst(s, String.valueOf(oldSub), newSub);
2667            }
2668    
2669            /**
2670             * Replaces the first occurrence of the string with the new string.
2671             *
2672             * @param  s the original string
2673             * @param  oldSub the string whose first occurrence in the original string
2674             *         is to be searched for and replaced
2675             * @param  newSub the string with which to replace the first occurrence of
2676             *         the <code>oldSub</code> string
2677             * @return a string representing the original string except with the first
2678             *         occurrence of the string <code>oldSub</code> replaced with the
2679             *         string <code>newSub</code>
2680             */
2681            public static String replaceFirst(String s, String oldSub, String newSub) {
2682                    return replaceFirst(s, oldSub, newSub, 0);
2683            }
2684    
2685            /**
2686             * Replaces the first occurrences of the elements of the string array with
2687             * the corresponding elements of the new string array, beginning the element
2688             * search from the index position.
2689             *
2690             * @param  s the original string
2691             * @param  oldSub the strings whose first occurrences are to be searched for
2692             *         and replaced in the original string
2693             * @param  newSub the strings with which to replace the first occurrences of
2694             *         the <code>oldSubs</code> strings
2695             * @param  fromIndex the start index within the string
2696             * @return a string representing the original string with the first
2697             *         occurrences of the <code>oldSubs</code> strings replaced with the
2698             *         corresponding <code>newSubs</code> strings, or <code>null</code>
2699             *         if the original string, the <code>oldSubs</code> string, or the
2700             *         <code>newSubs</code> string is <code>null</code>
2701             */
2702            public static String replaceFirst(
2703                    String s, String oldSub, String newSub, int fromIndex) {
2704    
2705                    if ((s == null) || (oldSub == null) || (newSub == null)) {
2706                            return null;
2707                    }
2708    
2709                    if (oldSub.equals(newSub)) {
2710                            return s;
2711                    }
2712    
2713                    int y = s.indexOf(oldSub, fromIndex);
2714    
2715                    if (y >= 0) {
2716                            return s.substring(0, y).concat(newSub).concat(
2717                                    s.substring(y + oldSub.length()));
2718                    }
2719                    else {
2720                            return s;
2721                    }
2722            }
2723    
2724            /**
2725             * Replaces the first occurrences of the elements of the string array with
2726             * the corresponding elements of the new string array.
2727             *
2728             * @param  s the original string
2729             * @param  oldSubs the strings whose first occurrences are to be searched
2730             *         for and replaced in the original string
2731             * @param  newSubs the strings with which to replace the first occurrences
2732             *         of the <code>oldSubs</code> strings
2733             * @return a string representing the original string with the first
2734             *         occurrences of the <code>oldSubs</code> strings replaced with the
2735             *         corresponding <code>newSubs</code> strings, or <code>null</code>
2736             *         if the original string, the <code>oldSubs</code> array, or the
2737             *         <code>newSubs</code> is <code>null</code>
2738             */
2739            public static String replaceFirst(
2740                    String s, String[] oldSubs, String[] newSubs) {
2741    
2742                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
2743                            return null;
2744                    }
2745    
2746                    if (oldSubs.length != newSubs.length) {
2747                            return s;
2748                    }
2749    
2750                    for (int i = 0; i < oldSubs.length; i++) {
2751                            s = replaceFirst(s, oldSubs[i], newSubs[i]);
2752                    }
2753    
2754                    return s;
2755            }
2756    
2757            /**
2758             * Replaces the last occurrence of the character with the new character.
2759             *
2760             * @param  s the original string
2761             * @param  oldSub the character whose last occurrence in the original string
2762             *         is to be searched for and replaced
2763             * @param  newSub the character with which to replace the last occurrence of
2764             *         the <code>oldSub</code> character
2765             * @return a string representing the original string except with the first
2766             *         occurrence of the character <code>oldSub</code> replaced with the
2767             *         character <code>newSub</code>
2768             */
2769            public static String replaceLast(String s, char oldSub, char newSub) {
2770                    if (s == null) {
2771                            return null;
2772                    }
2773    
2774                    return replaceLast(s, String.valueOf(oldSub), String.valueOf(newSub));
2775            }
2776    
2777            /**
2778             * Replaces the last occurrence of the character with the new string.
2779             *
2780             * @param  s the original string
2781             * @param  oldSub the character whose last occurrence in the original string
2782             *         is to be searched for and replaced
2783             * @param  newSub the string with which to replace the last occurrence of
2784             *         the <code>oldSub</code> character
2785             * @return a string representing the original string except with the last
2786             *         occurrence of the character <code>oldSub</code> replaced with the
2787             *         string <code>newSub</code>
2788             */
2789            public static String replaceLast(String s, char oldSub, String newSub) {
2790                    if ((s == null) || (newSub == null)) {
2791                            return null;
2792                    }
2793    
2794                    return replaceLast(s, String.valueOf(oldSub), newSub);
2795            }
2796    
2797            /**
2798             * Replaces the last occurrence of the string <code>oldSub</code> in the
2799             * string <code>s</code> with the string <code>newSub</code>.
2800             *
2801             * @param  s the original string
2802             * @param  oldSub the string whose last occurrence in the original string is
2803             *         to be searched for and replaced
2804             * @param  newSub the string with which to replace the last occurrence of
2805             *         the <code>oldSub</code> string
2806             * @return a string representing the original string except with the last
2807             *         occurrence of the string <code>oldSub</code> replaced with the
2808             *         string <code>newSub</code>
2809             */
2810            public static String replaceLast(String s, String oldSub, String newSub) {
2811                    if ((s == null) || (oldSub == null) || (newSub == null)) {
2812                            return null;
2813                    }
2814    
2815                    if (oldSub.equals(newSub)) {
2816                            return s;
2817                    }
2818    
2819                    int y = s.lastIndexOf(oldSub);
2820    
2821                    if (y >= 0) {
2822                            return s.substring(0, y).concat(newSub).concat(
2823                                    s.substring(y + oldSub.length()));
2824                    }
2825                    else {
2826                            return s;
2827                    }
2828            }
2829    
2830            /**
2831             * Replaces the last occurrences of the elements of the string array with
2832             * the corresponding elements of the new string array.
2833             *
2834             * @param  s the original string
2835             * @param  oldSubs the strings whose last occurrences are to be searched for
2836             *         and replaced in the original string
2837             * @param  newSubs the strings with which to replace the last occurrences of
2838             *         the <code>oldSubs</code> strings
2839             * @return a string representing the original string with the last
2840             *         occurrences of the <code>oldSubs</code> strings replaced with the
2841             *         corresponding <code>newSubs</code> strings, or <code>null</code>
2842             *         if the original string, the <code>oldSubs</code> array, or the
2843             *         <code>newSubs</code> is <code>null</code>
2844             */
2845            public static String replaceLast(
2846                    String s, String[] oldSubs, String[] newSubs) {
2847    
2848                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
2849                            return null;
2850                    }
2851    
2852                    if (oldSubs.length != newSubs.length) {
2853                            return s;
2854                    }
2855    
2856                    for (int i = 0; i < oldSubs.length; i++) {
2857                            s = replaceLast(s, oldSubs[i], newSubs[i]);
2858                    }
2859    
2860                    return s;
2861            }
2862    
2863            /**
2864             * Replaces all occurrences of the keywords found in the substring, defined
2865             * by the beginning and ending strings, with the new values. The result is
2866             * returned as a {@link StringBundler}.
2867             *
2868             * <p>
2869             * For example, with the following initialized variables:
2870             * </p>
2871             *
2872             * <p>
2873             * <pre>
2874             * <code>
2875             * String s = "http://www.example-url/${userId}";
2876             * String begin = "${";
2877             * String end = "}";
2878             * Map<String, String> values =  new HashMap&#60;String, String&#62;();
2879             * values.put("userId", "jbloggs");
2880             * </code>
2881             * </pre>
2882             * </p>
2883             *
2884             * <p>
2885             * <code>StringBundler sb = replaceToStringBundler(s, begin, end,
2886             * values)</code> <code>sb.toString()</code> returns
2887             * <code>"http://www.example-url/jbloggs"</code>
2888             * </p>
2889             *
2890             * @param  s the original string
2891             * @param  begin the string preceding the substring to be modified. This
2892             *         string is excluded from the result.
2893             * @param  end the string following the substring to be modified. This
2894             *         string is excluded from the result.
2895             * @param  values the key-value map values
2896             * @return a string bundler representing the original string with all
2897             *         occurrences of the keywords found in the substring, replaced with
2898             *         the new values. <code>null</code> is returned if the original
2899             *         string, the beginning string, the ending string, or the key-map
2900             *         values are <code>null</code>.
2901             * @see    #replace(String, String, String, Map)
2902             */
2903            public static StringBundler replaceToStringBundler(
2904                    String s, String begin, String end, Map<String, String> values) {
2905    
2906                    if (Validator.isBlank(s) || Validator.isBlank(begin) ||
2907                            Validator.isBlank(end) || MapUtil.isEmpty(values)) {
2908    
2909                            return new StringBundler(s);
2910                    }
2911    
2912                    StringBundler sb = new StringBundler(values.size() * 2 + 1);
2913    
2914                    int pos = 0;
2915    
2916                    while (true) {
2917                            int x = s.indexOf(begin, pos);
2918                            int y = s.indexOf(end, x + begin.length());
2919    
2920                            if ((x == -1) || (y == -1)) {
2921                                    sb.append(s.substring(pos));
2922    
2923                                    break;
2924                            }
2925                            else {
2926                                    sb.append(s.substring(pos, x));
2927    
2928                                    String oldValue = s.substring(x + begin.length(), y);
2929    
2930                                    String newValue = values.get(oldValue);
2931    
2932                                    if (newValue == null) {
2933                                            newValue = oldValue;
2934                                    }
2935    
2936                                    sb.append(newValue);
2937    
2938                                    pos = y + end.length();
2939                            }
2940                    }
2941    
2942                    return sb;
2943            }
2944    
2945            /**
2946             * Replaces all occurrences of the keywords found in the substring, defined
2947             * by the beginning and ending strings, with the new values. The result is
2948             * returned as a {@link StringBundler}.
2949             *
2950             * @param  s the original string
2951             * @param  begin the string preceding the substring to be modified. This
2952             *         string is removed from the result.
2953             * @param  end the string following the substring to be modified. This
2954             *         string is removed from the result.
2955             * @param  values the key-value map values, which has string keys and {@link
2956             *         StringBundler} values
2957             * @return a string bundler representing the original string with all
2958             *         occurrences of the keywords found in the substring, replaced with
2959             *         the new values. <code>null</code> is returned if the original
2960             *         string, the beginning string, the ending string, or the key-map
2961             *         values are <code>null</code>.
2962             */
2963            public static StringBundler replaceWithStringBundler(
2964                    String s, String begin, String end, Map<String, StringBundler> values) {
2965    
2966                    if (Validator.isBlank(s) || Validator.isBlank(begin) ||
2967                            Validator.isBlank(end) || MapUtil.isEmpty(values)) {
2968    
2969                            return new StringBundler(s);
2970                    }
2971    
2972                    int size = values.size() + 1;
2973    
2974                    for (StringBundler valueSB : values.values()) {
2975                            size += valueSB.index();
2976                    }
2977    
2978                    StringBundler sb = new StringBundler(size);
2979    
2980                    int pos = 0;
2981    
2982                    while (true) {
2983                            int x = s.indexOf(begin, pos);
2984                            int y = s.indexOf(end, x + begin.length());
2985    
2986                            if ((x == -1) || (y == -1)) {
2987                                    sb.append(s.substring(pos));
2988    
2989                                    break;
2990                            }
2991                            else {
2992                                    sb.append(s.substring(pos, x));
2993    
2994                                    String oldValue = s.substring(x + begin.length(), y);
2995    
2996                                    StringBundler newValue = values.get(oldValue);
2997    
2998                                    if (newValue == null) {
2999                                            sb.append(oldValue);
3000                                    }
3001                                    else {
3002                                            sb.append(newValue);
3003                                    }
3004    
3005                                    pos = y + end.length();
3006                            }
3007                    }
3008    
3009                    return sb;
3010            }
3011    
3012            /**
3013             * Reverses the order of the characters of the string.
3014             *
3015             * @param  s the original string
3016             * @return a string representing the original string with characters in
3017             *         reverse order
3018             */
3019            public static String reverse(String s) {
3020                    if (s == null) {
3021                            return null;
3022                    }
3023    
3024                    char[] chars = s.toCharArray();
3025                    char[] reverse = new char[chars.length];
3026    
3027                    for (int i = 0; i < chars.length; i++) {
3028                            reverse[i] = chars[chars.length - i - 1];
3029                    }
3030    
3031                    return new String(reverse);
3032            }
3033    
3034            /**
3035             * Replaces all double slashes of the string with single slashes.
3036             *
3037             * <p>
3038             * Example:
3039             * </p>
3040             *
3041             * <p>
3042             * <pre>
3043             * <code>
3044             * safePath("http://www.liferay.com") returns "http:/www.liferay.com"
3045             * </code>
3046             * </pre>
3047             * </p>
3048             *
3049             * @param  path the original string
3050             * @return a string representing the original string with all double slashes
3051             *         replaced with single slashes
3052             */
3053            public static String safePath(String path) {
3054                    return replace(path, StringPool.DOUBLE_SLASH, StringPool.SLASH);
3055            }
3056    
3057            /**
3058             * Returns a string representing the original string appended with suffix
3059             * "..." and then shortened to 20 characters.
3060             *
3061             * <p>
3062             * The suffix is only added if the original string exceeds 20 characters. If
3063             * the original string exceeds 20 characters and it contains whitespace, the
3064             * string is shortened at the first whitespace character.
3065             * </p>
3066             *
3067             * <p>
3068             * Examples:
3069             * </p>
3070             *
3071             * <p>
3072             * <pre>
3073             * <code>
3074             * shorten("12345678901234567890xyz") returns "12345678901234567..."
3075             * shorten("1 345678901234567890xyz") returns "1..."
3076             * shorten(" 2345678901234567890xyz") returns "..."
3077             * shorten("12345678901234567890") returns "12345678901234567890"
3078             * shorten(" 2345678901234567890") returns " 2345678901234567890"
3079             * </code>
3080             * </pre>
3081             * </p>
3082             *
3083             * @param  s the original string
3084             * @return a string representing the original string shortened to 20
3085             *         characters, with suffix "..." appended to it
3086             */
3087            public static String shorten(String s) {
3088                    return shorten(s, 20);
3089            }
3090    
3091            /**
3092             * Returns a string representing the original string appended with suffix
3093             * "..." and then shortened to the specified length.
3094             *
3095             * <p>
3096             * The suffix is only added if the original string exceeds the specified
3097             * length. If the original string exceeds the specified length and it
3098             * contains whitespace, the string is shortened at the first whitespace
3099             * character.
3100             * </p>
3101             *
3102             * <p>
3103             * Examples:
3104             * </p>
3105             *
3106             * <p>
3107             * <pre>
3108             * <code>
3109             * shorten("123456789", 8) returns "12345..."
3110             * shorten("1 3456789", 8) returns "1..."
3111             * shorten(" 23456789", 8) returns "..."
3112             * shorten("12345678", 8) returns "12345678"
3113             * shorten(" 1234567", 8) returns " 1234567"
3114             * </code>
3115             * </pre>
3116             * </p>
3117             *
3118             * @param  s the original string
3119             * @param  length the number of characters to limit from the original string
3120             * @return a string representing the original string shortened to the
3121             *         specified length, with suffix "..." appended to it
3122             */
3123            public static String shorten(String s, int length) {
3124                    return shorten(s, length, "...");
3125            }
3126    
3127            /**
3128             * Returns a string representing the original string appended with the
3129             * specified suffix and then shortened to the specified length.
3130             *
3131             * <p>
3132             * The suffix is only added if the original string exceeds the specified
3133             * length. If the original string exceeds the specified length and it
3134             * contains whitespace, the string is shortened at the first whitespace
3135             * character.
3136             * </p>
3137             *
3138             * <p>
3139             * Examples:
3140             * </p>
3141             *
3142             * <p>
3143             * <pre>
3144             * <code>
3145             * shorten("12345678901234", 13, "... etc.") returns "12345... etc."
3146             * shorten("1 345678901234", 13, "... etc.") returns "1... etc."
3147             * shorten(" 2345678901234", 13, "... etc.") returns "... etc."
3148             * shorten("1234567890123", 13, "... etc.") returns "1234567890123"
3149             * shorten(" 123456789012", 13, "... etc.") returns " 123456789012"
3150             * </code>
3151             * </pre>
3152             * </p>
3153             *
3154             * @param  s the original string
3155             * @param  length the number of characters to limit from the original string
3156             * @param  suffix the suffix to append
3157             * @return a string representing the original string shortened to the
3158             *         specified length, with the specified suffix appended to it
3159             */
3160            public static String shorten(String s, int length, String suffix) {
3161                    if ((s == null) || (suffix == null)) {
3162                            return null;
3163                    }
3164    
3165                    if (s.length() <= length) {
3166                            return s;
3167                    }
3168    
3169                    if (length < suffix.length()) {
3170                            return s.substring(0, length);
3171                    }
3172    
3173                    int curLength = length;
3174    
3175                    for (int j = (curLength - suffix.length()); j >= 0; j--) {
3176                            if (Character.isWhitespace(s.charAt(j))) {
3177                                    curLength = j;
3178    
3179                                    break;
3180                            }
3181                    }
3182    
3183                    if (curLength == length) {
3184                            curLength = length - suffix.length();
3185                    }
3186    
3187                    String temp = s.substring(0, curLength);
3188    
3189                    return temp.concat(suffix);
3190            }
3191    
3192            /**
3193             * Returns a string representing the original string appended with the
3194             * specified suffix and then shortened to 20 characters.
3195             *
3196             * <p>
3197             * The suffix is only added if the original string exceeds 20 characters. If
3198             * the original string exceeds 20 characters and it contains whitespace, the
3199             * string is shortened at the first whitespace character.
3200             * </p>
3201             *
3202             * <p>
3203             * Examples:
3204             * </p>
3205             *
3206             * <p>
3207             * <pre>
3208             * <code>
3209             * shorten("12345678901234567890xyz", "... etc.") returns "123456789012... etc."
3210             * shorten("1 345678901234567890xyz", "... etc.") returns "1... etc."
3211             * shorten(" 2345678901234567890xyz", "... etc.") returns "... etc."
3212             * shorten("12345678901234567890", "... etc.") returns "12345678901234567890"
3213             * shorten(" 2345678901234567890", "... etc.") returns " 2345678901234567890"
3214             * </code>
3215             * </pre>
3216             * </p>
3217             *
3218             * @param  s the original string
3219             * @param  suffix the suffix to append
3220             * @return a string representing the original string shortened to 20
3221             *         characters, with the specified suffix appended to it
3222             */
3223            public static String shorten(String s, String suffix) {
3224                    return shorten(s, 20, suffix);
3225            }
3226    
3227            /**
3228             * Splits string <code>s</code> around comma characters.
3229             *
3230             * <p>
3231             * Examples:
3232             * </p>
3233             *
3234             * <p>
3235             * <pre>
3236             * <code>
3237             * split("Alice,Bob,Charlie") returns {"Alice", "Bob", "Charlie"}
3238             * split("Alice, Bob, Charlie") returns {"Alice", " Bob", " Charlie"}
3239             * </code>
3240             * </pre>
3241             * </p>
3242             *
3243             * @param  s the string to split
3244             * @return the array of strings resulting from splitting string
3245             *         <code>s</code> around comma characters, or an empty string array
3246             *         if <code>s</code> is <code>null</code> or <code>s</code> is empty
3247             */
3248            public static String[] split(String s) {
3249                    return split(s, CharPool.COMMA);
3250            }
3251    
3252            /**
3253             * Splits the string <code>s</code> around comma characters returning the
3254             * boolean values of the substrings.
3255             *
3256             * @param  s the string to split
3257             * @param  x the default value to use for a substring in case an exception
3258             *         occurs in getting the boolean value for that substring
3259             * @return the array of boolean values resulting from splitting string
3260             *         <code>s</code> around comma characters, or an empty array if
3261             *         <code>s</code> is <code>null</code>
3262             */
3263            public static boolean[] split(String s, boolean x) {
3264                    return split(s, StringPool.COMMA, x);
3265            }
3266    
3267            /**
3268             * Splits the string <code>s</code> around the specified delimiter.
3269             *
3270             * <p>
3271             * Example:
3272             * </p>
3273             *
3274             * <p>
3275             * <pre>
3276             * <code>
3277             * splitLines("First;Second;Third", ';') returns {"First","Second","Third"}
3278             * </code>
3279             * </pre>
3280             * </p>
3281             *
3282             * @param  s the string to split
3283             * @param  delimiter the delimiter
3284             * @return the array of strings resulting from splitting string
3285             *         <code>s</code> around the specified delimiter character, or an
3286             *         empty string array if <code>s</code> is <code>null</code> or if
3287             *         <code>s</code> is empty
3288             */
3289            public static String[] split(String s, char delimiter) {
3290                    if (Validator.isNull(s)) {
3291                            return _emptyStringArray;
3292                    }
3293    
3294                    s = s.trim();
3295    
3296                    if (s.length() == 0) {
3297                            return _emptyStringArray;
3298                    }
3299    
3300                    if ((delimiter == CharPool.RETURN) ||
3301                            (delimiter == CharPool.NEW_LINE)) {
3302    
3303                            return splitLines(s);
3304                    }
3305    
3306                    List<String> nodeValues = new ArrayList<>();
3307    
3308                    int offset = 0;
3309                    int pos = s.indexOf(delimiter, offset);
3310    
3311                    while (pos != -1) {
3312                            nodeValues.add(s.substring(offset, pos));
3313    
3314                            offset = pos + 1;
3315                            pos = s.indexOf(delimiter, offset);
3316                    }
3317    
3318                    if (offset < s.length()) {
3319                            nodeValues.add(s.substring(offset));
3320                    }
3321    
3322                    return nodeValues.toArray(new String[nodeValues.size()]);
3323            }
3324    
3325            /**
3326             * Splits the string <code>s</code> around comma characters returning the
3327             * double-precision decimal values of the substrings.
3328             *
3329             * @param  s the string to split
3330             * @param  x the default value to use for a substring in case an exception
3331             *         occurs in getting the double-precision decimal value for that
3332             *         substring
3333             * @return the array of double-precision decimal values resulting from
3334             *         splitting string <code>s</code> around comma characters, or an
3335             *         empty array if <code>s</code> is <code>null</code>
3336             */
3337            public static double[] split(String s, double x) {
3338                    return split(s, StringPool.COMMA, x);
3339            }
3340    
3341            /**
3342             * Splits the string <code>s</code> around comma characters returning the
3343             * decimal values of the substrings.
3344             *
3345             * @param  s the string to split
3346             * @param  x the default value to use for a substring in case an exception
3347             *         occurs in getting the decimal value for that substring
3348             * @return the array of decimal values resulting from splitting string
3349             *         <code>s</code> around comma characters, or an empty array if
3350             *         <code>s</code> is <code>null</code>
3351             */
3352            public static float[] split(String s, float x) {
3353                    return split(s, StringPool.COMMA, x);
3354            }
3355    
3356            /**
3357             * Splits the string <code>s</code> around comma characters returning the
3358             * integer values of the substrings.
3359             *
3360             * @param  s the string to split
3361             * @param  x the default value to use for a substring in case an exception
3362             *         occurs in getting the integer value for that substring
3363             * @return the array of integer values resulting from splitting string
3364             *         <code>s</code> around comma characters, or an empty array if
3365             *         <code>s</code> is <code>null</code>
3366             */
3367            public static int[] split(String s, int x) {
3368                    return split(s, StringPool.COMMA, x);
3369            }
3370    
3371            /**
3372             * Splits the string <code>s</code> around comma characters returning the
3373             * long integer values of the substrings.
3374             *
3375             * @param  s the string to split
3376             * @param  x the default value to use for a substring in case an exception
3377             *         occurs in getting the long integer value for that substring
3378             * @return the array of long integer values resulting from splitting string
3379             *         <code>s</code> around comma characters, or an empty array if
3380             *         <code>s</code> is <code>null</code>
3381             */
3382            public static long[] split(String s, long x) {
3383                    return split(s, StringPool.COMMA, x);
3384            }
3385    
3386            /**
3387             * Splits the string <code>s</code> around comma characters returning the
3388             * short integer values of the substrings.
3389             *
3390             * @param  s the string to split
3391             * @param  x the default value to use for a substring in case an exception
3392             *         occurs in getting the short integer value for that substring
3393             * @return the array of short integer values resulting from splitting string
3394             *         <code>s</code> around comma characters, or an empty array if
3395             *         <code>s</code> is <code>null</code>
3396             */
3397            public static short[] split(String s, short x) {
3398                    return split(s, StringPool.COMMA, x);
3399            }
3400    
3401            /**
3402             * Splits the string <code>s</code> around the specified delimiter string.
3403             *
3404             * <p>
3405             * Example:
3406             * </p>
3407             *
3408             * <p>
3409             * <pre>
3410             * <code>
3411             * splitLines("oneandtwoandthreeandfour", "and") returns {"one","two","three","four"}
3412             * </code>
3413             * </pre>
3414             * </p>
3415             *
3416             * @param  s the string to split
3417             * @param  delimiter the delimiter
3418             * @return the array of strings resulting from splitting string
3419             *         <code>s</code> around the specified delimiter string, or an empty
3420             *         string array if <code>s</code> is <code>null</code> or equals the
3421             *         delimiter
3422             */
3423            public static String[] split(String s, String delimiter) {
3424                    if (Validator.isNull(s) || (delimiter == null) ||
3425                            delimiter.equals(StringPool.BLANK)) {
3426    
3427                            return _emptyStringArray;
3428                    }
3429    
3430                    s = s.trim();
3431    
3432                    if (s.equals(delimiter)) {
3433                            return _emptyStringArray;
3434                    }
3435    
3436                    if (delimiter.length() == 1) {
3437                            return split(s, delimiter.charAt(0));
3438                    }
3439    
3440                    List<String> nodeValues = new ArrayList<>();
3441    
3442                    int offset = 0;
3443                    int pos = s.indexOf(delimiter, offset);
3444    
3445                    while (pos != -1) {
3446                            nodeValues.add(s.substring(offset, pos));
3447    
3448                            offset = pos + delimiter.length();
3449                            pos = s.indexOf(delimiter, offset);
3450                    }
3451    
3452                    if (offset < s.length()) {
3453                            nodeValues.add(s.substring(offset));
3454                    }
3455    
3456                    return nodeValues.toArray(new String[nodeValues.size()]);
3457            }
3458    
3459            /**
3460             * Splits the string <code>s</code> around the specified delimiter returning
3461             * the boolean values of the substrings.
3462             *
3463             * @param  s the string to split
3464             * @param  delimiter the delimiter
3465             * @param  x the default value to use for a substring in case an exception
3466             *         occurs in getting the boolean value for that substring
3467             * @return the array of booleans resulting from splitting string
3468             *         <code>s</code> around the specified delimiter string, or an empty
3469             *         array if <code>s</code> is <code>null</code>
3470             */
3471            public static boolean[] split(String s, String delimiter, boolean x) {
3472                    String[] array = split(s, delimiter);
3473                    boolean[] newArray = new boolean[array.length];
3474    
3475                    for (int i = 0; i < array.length; i++) {
3476                            boolean value = x;
3477    
3478                            try {
3479                                    value = Boolean.valueOf(array[i]).booleanValue();
3480                            }
3481                            catch (Exception e) {
3482                            }
3483    
3484                            newArray[i] = value;
3485                    }
3486    
3487                    return newArray;
3488            }
3489    
3490            /**
3491             * Splits the string <code>s</code> around the specified delimiter returning
3492             * the double-precision decimal values of the substrings.
3493             *
3494             * @param  s the string to split
3495             * @param  delimiter the delimiter
3496             * @param  x the default value to use for a substring in case an exception
3497             *         occurs in getting the double-precision decimal value for that
3498             *         substring
3499             * @return the array of double-precision decimal values resulting from
3500             *         splitting string <code>s</code> around the specified delimiter
3501             *         string, or an empty array if <code>s</code> is <code>null</code>
3502             */
3503            public static double[] split(String s, String delimiter, double x) {
3504                    String[] array = split(s, delimiter);
3505                    double[] newArray = new double[array.length];
3506    
3507                    for (int i = 0; i < array.length; i++) {
3508                            double value = x;
3509    
3510                            try {
3511                                    value = Double.parseDouble(array[i]);
3512                            }
3513                            catch (Exception e) {
3514                            }
3515    
3516                            newArray[i] = value;
3517                    }
3518    
3519                    return newArray;
3520            }
3521    
3522            /**
3523             * Splits the string <code>s</code> around the specified delimiter returning
3524             * the decimal values of the substrings.
3525             *
3526             * @param  s the string to split
3527             * @param  delimiter the delimiter
3528             * @param  x the default value to use for a substring in case an exception
3529             *         occurs in getting the decimal value for that substring
3530             * @return the array of decimal values resulting from splitting string
3531             *         <code>s</code> around the specified delimiter string, or an empty
3532             *         array if <code>s</code> is <code>null</code>
3533             */
3534            public static float[] split(String s, String delimiter, float x) {
3535                    String[] array = split(s, delimiter);
3536                    float[] newArray = new float[array.length];
3537    
3538                    for (int i = 0; i < array.length; i++) {
3539                            float value = x;
3540    
3541                            try {
3542                                    value = Float.parseFloat(array[i]);
3543                            }
3544                            catch (Exception e) {
3545                            }
3546    
3547                            newArray[i] = value;
3548                    }
3549    
3550                    return newArray;
3551            }
3552    
3553            /**
3554             * Splits the string <code>s</code> around the specified delimiter returning
3555             * the integer values of the substrings.
3556             *
3557             * @param  s the string to split
3558             * @param  delimiter the delimiter
3559             * @param  x the default value to use for a substring in case an exception
3560             *         occurs in getting the integer value for that substring
3561             * @return the array of integer values resulting from splitting string
3562             *         <code>s</code> around the specified delimiter string, or an empty
3563             *         array if <code>s</code> is <code>null</code>
3564             */
3565            public static int[] split(String s, String delimiter, int x) {
3566                    String[] array = split(s, delimiter);
3567                    int[] newArray = new int[array.length];
3568    
3569                    for (int i = 0; i < array.length; i++) {
3570                            int value = x;
3571    
3572                            try {
3573                                    value = Integer.parseInt(array[i]);
3574                            }
3575                            catch (Exception e) {
3576                            }
3577    
3578                            newArray[i] = value;
3579                    }
3580    
3581                    return newArray;
3582            }
3583    
3584            /**
3585             * Splits the string <code>s</code> around the specified delimiter returning
3586             * the long integer values of the substrings.
3587             *
3588             * @param  s the string to split
3589             * @param  delimiter the delimiter
3590             * @param  x the default value to use for a substring in case an exception
3591             *         occurs in getting the long integer value for that substring
3592             * @return the array of long integer values resulting from splitting string
3593             *         <code>s</code> around the specified delimiter string, or an empty
3594             *         array if <code>s</code> is <code>null</code>
3595             */
3596            public static long[] split(String s, String delimiter, long x) {
3597                    String[] array = split(s, delimiter);
3598                    long[] newArray = new long[array.length];
3599    
3600                    for (int i = 0; i < array.length; i++) {
3601                            long value = x;
3602    
3603                            try {
3604                                    value = Long.parseLong(array[i]);
3605                            }
3606                            catch (Exception e) {
3607                            }
3608    
3609                            newArray[i] = value;
3610                    }
3611    
3612                    return newArray;
3613            }
3614    
3615            /**
3616             * Splits the string <code>s</code> around the specified delimiter returning
3617             * the short integer values of the substrings.
3618             *
3619             * @param  s the string to split
3620             * @param  delimiter the delimiter
3621             * @param  x the default value to use for a substring in case an exception
3622             *         occurs in getting the short integer value for that substring
3623             * @return the array of short integer values resulting from splitting string
3624             *         <code>s</code> around the specified delimiter string, or an empty
3625             *         array if <code>s</code> is <code>null</code>
3626             */
3627            public static short[] split(String s, String delimiter, short x) {
3628                    String[] array = split(s, delimiter);
3629                    short[] newArray = new short[array.length];
3630    
3631                    for (int i = 0; i < array.length; i++) {
3632                            short value = x;
3633    
3634                            try {
3635                                    value = Short.parseShort(array[i]);
3636                            }
3637                            catch (Exception e) {
3638                            }
3639    
3640                            newArray[i] = value;
3641                    }
3642    
3643                    return newArray;
3644            }
3645    
3646            /**
3647             * Splits string <code>s</code> around return and newline characters.
3648             *
3649             * <p>
3650             * Example:
3651             * </p>
3652             *
3653             * <p>
3654             * <pre>
3655             * <code>
3656             * splitLines("Red\rBlue\nGreen") returns {"Red","Blue","Green"}
3657             * </code>
3658             * </pre>
3659             * </p>
3660             *
3661             * @param  s the string to split
3662             * @return the array of strings resulting from splitting string
3663             *         <code>s</code> around return and newline characters, or an empty
3664             *         string array if string <code>s</code> is <code>null</code>
3665             */
3666            public static String[] splitLines(String s) {
3667                    if (Validator.isNull(s)) {
3668                            return _emptyStringArray;
3669                    }
3670    
3671                    s = s.trim();
3672    
3673                    List<String> lines = new ArrayList<>();
3674    
3675                    int lastIndex = 0;
3676    
3677                    while (true) {
3678                            int returnIndex = s.indexOf(CharPool.RETURN, lastIndex);
3679                            int newLineIndex = s.indexOf(CharPool.NEW_LINE, lastIndex);
3680    
3681                            if ((returnIndex == -1) && (newLineIndex == -1)) {
3682                                    break;
3683                            }
3684    
3685                            if (returnIndex == -1) {
3686                                    lines.add(s.substring(lastIndex, newLineIndex));
3687    
3688                                    lastIndex = newLineIndex + 1;
3689                            }
3690                            else if (newLineIndex == -1) {
3691                                    lines.add(s.substring(lastIndex, returnIndex));
3692    
3693                                    lastIndex = returnIndex + 1;
3694                            }
3695                            else if (newLineIndex < returnIndex) {
3696                                    lines.add(s.substring(lastIndex, newLineIndex));
3697    
3698                                    lastIndex = newLineIndex + 1;
3699                            }
3700                            else {
3701                                    lines.add(s.substring(lastIndex, returnIndex));
3702    
3703                                    lastIndex = returnIndex + 1;
3704    
3705                                    if (lastIndex == newLineIndex) {
3706                                            lastIndex++;
3707                                    }
3708                            }
3709                    }
3710    
3711                    if (lastIndex < s.length()) {
3712                            lines.add(s.substring(lastIndex));
3713                    }
3714    
3715                    return lines.toArray(new String[lines.size()]);
3716            }
3717    
3718            /**
3719             * Returns <code>true</code> if, ignoring case, the string starts with the
3720             * specified character.
3721             *
3722             * @param  s the string
3723             * @param  begin the character against which the initial character of the
3724             *         string is to be compared
3725             * @return <code>true</code> if, ignoring case, the string starts with the
3726             *         specified character; <code>false</code> otherwise
3727             */
3728            public static boolean startsWith(String s, char begin) {
3729                    return startsWith(s, (Character.valueOf(begin)).toString());
3730            }
3731    
3732            /**
3733             * Returns <code>true</code> if, ignoring case, the string starts with the
3734             * specified start string.
3735             *
3736             * @param  s the original string
3737             * @param  start the string against which the beginning of string
3738             *         <code>s</code> are to be compared
3739             * @return <code>true</code> if, ignoring case, the string starts with the
3740             *         specified start string; <code>false</code> otherwise
3741             */
3742            public static boolean startsWith(String s, String start) {
3743                    if ((s == null) || (start == null)) {
3744                            return false;
3745                    }
3746    
3747                    if (start.length() > s.length()) {
3748                            return false;
3749                    }
3750    
3751                    String temp = s.substring(0, start.length());
3752    
3753                    if (equalsIgnoreCase(temp, start)) {
3754                            return true;
3755                    }
3756                    else {
3757                            return false;
3758                    }
3759            }
3760    
3761            /**
3762             * Returns the number of starting characters that <code>s1</code> and
3763             * <code>s2</code> have in common before their characters deviate.
3764             *
3765             * @param  s1 string 1
3766             * @param  s2 string 2
3767             * @return the number of starting characters that <code>s1</code> and
3768             *         <code>s2</code> have in common before their characters deviate
3769             */
3770            public static int startsWithWeight(String s1, String s2) {
3771                    if ((s1 == null) || (s2 == null)) {
3772                            return 0;
3773                    }
3774    
3775                    char[] chars1 = s1.toCharArray();
3776                    char[] chars2 = s2.toCharArray();
3777    
3778                    int i = 0;
3779    
3780                    for (; (i < chars1.length) && (i < chars2.length); i++) {
3781                            if (chars1[i] != chars2[i]) {
3782                                    break;
3783                            }
3784                    }
3785    
3786                    return i;
3787            }
3788    
3789            /**
3790             * Returns a string representing the string <code>s</code> with all
3791             * occurrences of the specified character removed.
3792             *
3793             * <p>
3794             * Example:
3795             * </p>
3796             *
3797             * <p>
3798             * <pre>
3799             * <code>
3800             * strip("Mississipi", 'i') returns "Mssssp"
3801             * </code>
3802             * </pre>
3803             * </p>
3804             *
3805             * @param  s the string from which to strip all occurrences of the character
3806             * @param  remove the character to strip from the string
3807             * @return a string representing the string <code>s</code> with all
3808             *         occurrences of the specified character removed, or
3809             *         <code>null</code> if <code>s</code> is <code>null</code>
3810             */
3811            public static String strip(String s, char remove) {
3812                    if (s == null) {
3813                            return null;
3814                    }
3815    
3816                    int x = s.indexOf(remove);
3817    
3818                    if (x < 0) {
3819                            return s;
3820                    }
3821    
3822                    int y = 0;
3823    
3824                    StringBundler sb = new StringBundler(s.length());
3825    
3826                    while (x >= 0) {
3827                            sb.append(s.subSequence(y, x));
3828    
3829                            y = x + 1;
3830    
3831                            x = s.indexOf(remove, y);
3832                    }
3833    
3834                    sb.append(s.substring(y));
3835    
3836                    return sb.toString();
3837            }
3838    
3839            /**
3840             * Returns a string representing the string <code>s</code> with all
3841             * occurrences of the specified characters removed.
3842             *
3843             * <p>
3844             * Example:
3845             * </p>
3846             *
3847             * <p>
3848             * <pre>
3849             * <code>
3850             * strip("Hello World", {' ', 'l', 'd'}) returns "HeoWor"
3851             * </code>
3852             * </pre>
3853             * </p>
3854             *
3855             * @param  s the string from which to strip all occurrences the characters
3856             * @param  remove the characters to strip from the string
3857             * @return a string representing the string <code>s</code> with all
3858             *         occurrences of the specified characters removed, or
3859             *         <code>null</code> if <code>s</code> is <code>null</code>
3860             */
3861            public static String strip(String s, char[] remove) {
3862                    for (char c : remove) {
3863                            s = strip(s, c);
3864                    }
3865    
3866                    return s;
3867            }
3868    
3869            /**
3870             * Returns a string representing the combination of the substring of
3871             * <code>s</code> up to but not including the string <code>begin</code>
3872             * concatenated with the substring of <code>s</code> after but not including
3873             * the string <code>end</code>.
3874             *
3875             * <p>
3876             * Example:
3877             * </p>
3878             *
3879             * <p>
3880             * <pre>
3881             * <code>
3882             * stripBetween("One small step for man, one giant leap for mankind", "step", "giant ") returns "One small leap for mankind"
3883             * </code>
3884             * </pre>
3885             * </p>
3886             *
3887             * @param  s the string from which to strip a substring
3888             * @param  begin the beginning characters of the substring to be removed
3889             * @param  end the ending characters of the substring to be removed
3890             * @return a string representing the combination of the substring of
3891             *         <code>s</code> up to but not including the string
3892             *         <code>begin</code> concatenated with the substring of
3893             *         <code>s</code> after but not including the string
3894             *         <code>end</code>, or the original string if the value of
3895             *         <code>s</code>, <code>begin</code>, or <code>end</code> are
3896             *         <code>null</code>
3897             */
3898            public static String stripBetween(String s, String begin, String end) {
3899                    if (Validator.isBlank(s) || Validator.isBlank(begin) ||
3900                            Validator.isBlank(end)) {
3901    
3902                            return s;
3903                    }
3904    
3905                    StringBundler sb = new StringBundler(s.length());
3906    
3907                    int pos = 0;
3908    
3909                    while (true) {
3910                            int x = s.indexOf(begin, pos);
3911                            int y = s.indexOf(end, x + begin.length());
3912    
3913                            if ((x == -1) || (y == -1)) {
3914                                    sb.append(s.substring(pos));
3915    
3916                                    break;
3917                            }
3918                            else {
3919                                    sb.append(s.substring(pos, x));
3920    
3921                                    pos = y + end.length();
3922                            }
3923                    }
3924    
3925                    return sb.toString();
3926            }
3927    
3928            /**
3929             * Returns a string representing the string <code>s</code> with its
3930             * <code>&lt;![CDATA[]]&gt;</code> wrapper removed.
3931             *
3932             * <p>
3933             * Example:
3934             * </p>
3935             *
3936             * <p>
3937             * <pre>
3938             * <code>
3939             * stripCDATA("&lt;![CDATA[One small step for man]]&gt;") returns "One small step for man"
3940             * </code>
3941             * </pre>
3942             * </p>
3943             *
3944             * @param  s the string from which to strip its CDATA wrapper
3945             * @return a string representing the string <code>s</code> with its
3946             *         <code>&lt;![CDATA[]]&gt;</code> wrapper removed, or
3947             *         <code>null</code> if <code>s</code> is <code>null</code>
3948             */
3949            public static String stripCDATA(String s) {
3950                    if (s == null) {
3951                            return s;
3952                    }
3953    
3954                    if (s.startsWith(StringPool.CDATA_OPEN) &&
3955                            s.endsWith(StringPool.CDATA_CLOSE)) {
3956    
3957                            s = s.substring(
3958                                    StringPool.CDATA_OPEN.length(),
3959                                    s.length() - StringPool.CDATA_CLOSE.length());
3960                    }
3961    
3962                    return s;
3963            }
3964    
3965            /**
3966             * Returns a string representing the string <code>s</code> without an
3967             * appended parenthetical suffix. If there is not a space directly before
3968             * the opening parenthesis, the parenthetical suffix is not stripped.
3969             *
3970             * <p>
3971             * Examples:
3972             * </p>
3973             *
3974             * <p>
3975             * <pre>
3976             * <code>
3977             * stripParentheticalSuffix("file") returns "file"
3978             * stripParentheticalSuffix("file (0)") returns "file"
3979             * stripParentheticalSuffix("file (0 0)") returns "file"
3980             * stripParentheticalSuffix("file(0)") returns "file(0)"
3981             * </code>
3982             * </pre>
3983             * </p>
3984             *
3985             * @param  s the string from which to strip its parenthetical suffix
3986             * @return a string representing the string <code>s</code> without an
3987             *         appended parenthetical suffix
3988             */
3989            public static String stripParentheticalSuffix(String s) {
3990                    int x = s.lastIndexOf(StringPool.OPEN_PARENTHESIS);
3991                    int y = s.lastIndexOf(StringPool.CLOSE_PARENTHESIS);
3992    
3993                    if ((x == -1) || (y == -1)) {
3994                            return s;
3995                    }
3996    
3997                    if ((x > y) || !s.endsWith(StringPool.CLOSE_PARENTHESIS)) {
3998                            return s;
3999                    }
4000    
4001                    if (s.charAt(x - 1) != CharPool.SPACE) {
4002                            return s;
4003                    }
4004    
4005                    return s.substring(0, x - 1).concat(s.substring(y + 1, s.length()));
4006            }
4007    
4008            /**
4009             * Returns a string representing the Unicode character codes of the
4010             * characters comprising the string <code>s</code>.
4011             *
4012             * <p>
4013             * Examples:
4014             * </p>
4015             *
4016             * <p>
4017             * <pre>
4018             * <code>
4019             * toCharCode("a") returns "97"
4020             * toCharCode("b") returns "98"
4021             * toCharCode("c") returns "99"
4022             * toCharCode("What's for lunch?") returns "87104971163911532102111114321081171109910463"
4023             * </code>
4024             * </pre>
4025             * </p>
4026             *
4027             * @param  s the string whose character codes are to be represented
4028             * @return a string representing the Unicode character codes of the
4029             *         characters comprising the string <code>s</code>
4030             */
4031            public static String toCharCode(String s) {
4032                    StringBundler sb = new StringBundler(s.length());
4033    
4034                    for (int i = 0; i < s.length(); i++) {
4035                            sb.append(s.codePointAt(i));
4036                    }
4037    
4038                    return sb.toString();
4039            }
4040    
4041            /**
4042             * Returns a string representing the hexidecimal character code of the
4043             * integer.
4044             *
4045             * <p>
4046             * Examples:
4047             * </p>
4048             *
4049             * <p>
4050             * <pre>
4051             * <code>
4052             * toHexString(10) returns "a"
4053             * toHexString(15) returns "f"
4054             * toHexString(10995) returns "2af3"
4055             * </code>
4056             * </pre>
4057             * </p>
4058             *
4059             * @param  i the integer to convert
4060             * @return a string representing the hexidecimal character code of the
4061             *         integer
4062             */
4063            public static String toHexString(int i) {
4064                    char[] buffer = new char[8];
4065    
4066                    int index = 8;
4067    
4068                    do {
4069                            buffer[--index] = HEX_DIGITS[i & 15];
4070    
4071                            i >>>= 4;
4072                    }
4073                    while (i != 0);
4074    
4075                    return new String(buffer, index, 8 - index);
4076            }
4077    
4078            /**
4079             * Returns a string representing the hexidecimal character code of the long
4080             * integer.
4081             *
4082             * <p>
4083             * Example:
4084             * </p>
4085             *
4086             * <p>
4087             * <pre>
4088             * <code>
4089             * toHexString(12345678910L) returns "2dfdc1c3e"
4090             * </code>
4091             * </pre>
4092             * </p>
4093             *
4094             * @param  l the long integer to convert
4095             * @return a string representing the hexidecimal character code of the long
4096             *         integer
4097             */
4098            public static String toHexString(long l) {
4099                    char[] buffer = new char[16];
4100    
4101                    int index = 16;
4102    
4103                    do {
4104                            buffer[--index] = HEX_DIGITS[(int) (l & 15)];
4105    
4106                            l >>>= 4;
4107                    }
4108                    while (l != 0);
4109    
4110                    return new String(buffer, index, 16 - index);
4111            }
4112    
4113            /**
4114             * Returns a string representing the hexidecimal character code of the
4115             * <code>Integer</code> or <code>Long</code> object type. If the object is
4116             * not an instance of these types, the object's original value is returned.
4117             *
4118             * @param  obj the object to convert
4119             * @return a string representing the hexidecimal character code of the
4120             *         object
4121             */
4122            public static String toHexString(Object obj) {
4123                    if (obj instanceof Integer) {
4124                            return toHexString(((Integer)obj).intValue());
4125                    }
4126                    else if (obj instanceof Long) {
4127                            return toHexString(((Long)obj).longValue());
4128                    }
4129                    else {
4130                            return String.valueOf(obj);
4131                    }
4132            }
4133    
4134            /**
4135             * Converts all of the characters in the string to lower case, based on the
4136             * portal instance's default locale.
4137             *
4138             * @param  s the string to convert
4139             * @return the string, converted to lower case, or <code>null</code> if the
4140             *         string is <code>null</code>
4141             */
4142            public static String toLowerCase(String s) {
4143                    return toLowerCase(s, null);
4144            }
4145    
4146            /**
4147             * Converts all of the characters in the string to lower case, based on the
4148             * locale.
4149             *
4150             * @param  s the string to convert
4151             * @param  locale apply this locale's rules
4152             * @return the string, converted to lower case, or <code>null</code> if the
4153             *         string is <code>null</code>
4154             */
4155            public static String toLowerCase(String s, Locale locale) {
4156                    if (s == null) {
4157                            return null;
4158                    }
4159    
4160                    StringBuilder sb = null;
4161    
4162                    for (int i = 0; i < s.length(); i++) {
4163                            char c = s.charAt(i);
4164    
4165                            if (c > 127) {
4166    
4167                                    // Found non-ascii char, fallback to the slow unicode detection
4168    
4169                                    if (locale == null) {
4170                                            locale = LocaleUtil.getDefault();
4171                                    }
4172    
4173                                    return s.toLowerCase(locale);
4174                            }
4175    
4176                            if ((c >= 'A') && (c <= 'Z')) {
4177                                    if (sb == null) {
4178                                            sb = new StringBuilder(s);
4179                                    }
4180    
4181                                    sb.setCharAt(i, (char)(c + 32));
4182                            }
4183                    }
4184    
4185                    if (sb == null) {
4186                            return s;
4187                    }
4188    
4189                    return sb.toString();
4190            }
4191    
4192            /**
4193             * Converts all of the characters in the string to upper case, based on the
4194             * portal instance's default locale.
4195             *
4196             * @param  s the string to convert
4197             * @return the string, converted to upper case, or <code>null</code> if the
4198             *         string is <code>null</code>
4199             */
4200            public static String toUpperCase(String s) {
4201                    return toUpperCase(s, null);
4202            }
4203    
4204            /**
4205             * Converts all of the characters in the string to upper case, based on the
4206             * locale.
4207             *
4208             * @param  s the string to convert
4209             * @param  locale apply this locale's rules
4210             * @return the string, converted to upper case, or <code>null</code> if the
4211             *         string is <code>null</code>
4212             */
4213            public static String toUpperCase(String s, Locale locale) {
4214                    if (s == null) {
4215                            return null;
4216                    }
4217    
4218                    StringBuilder sb = null;
4219    
4220                    for (int i = 0; i < s.length(); i++) {
4221                            char c = s.charAt(i);
4222    
4223                            if (c > 127) {
4224    
4225                                    // Found non-ascii char, fallback to the slow unicode detection
4226    
4227                                    if (locale == null) {
4228                                            locale = LocaleUtil.getDefault();
4229                                    }
4230    
4231                                    return s.toUpperCase(locale);
4232                            }
4233    
4234                            if ((c >= 'a') && (c <= 'z')) {
4235                                    if (sb == null) {
4236                                            sb = new StringBuilder(s);
4237                                    }
4238    
4239                                    sb.setCharAt(i, (char)(c - 32));
4240                            }
4241                    }
4242    
4243                    if (sb == null) {
4244                            return s;
4245                    }
4246    
4247                    return sb.toString();
4248            }
4249    
4250            /**
4251             * Trims all leading and trailing whitespace from the string.
4252             *
4253             * @param  s the original string
4254             * @return a string representing the original string with all leading and
4255             *         trailing whitespace removed
4256             */
4257            public static String trim(String s) {
4258                    if (s == null) {
4259                            return null;
4260                    }
4261    
4262                    if (s.length() == 0) {
4263                            return s;
4264                    }
4265    
4266                    int len = s.length();
4267    
4268                    int x = len;
4269    
4270                    for (int i = 0; i < len; i++) {
4271                            char c = s.charAt(i);
4272    
4273                            if (!Character.isWhitespace(c)) {
4274                                    x = i;
4275    
4276                                    break;
4277                            }
4278                    }
4279    
4280                    if (x == len) {
4281                            return StringPool.BLANK;
4282                    }
4283    
4284                    int y = x + 1;
4285    
4286                    for (int i = len - 1; i > x; i--) {
4287                            char c = s.charAt(i);
4288    
4289                            if (!Character.isWhitespace(c)) {
4290                                    y = i + 1;
4291    
4292                                    break;
4293                            }
4294                    }
4295    
4296                    if ((x == 0) && (y == len)) {
4297                            return s;
4298                    }
4299    
4300                    return s.substring(x, y);
4301            }
4302    
4303            /**
4304             * Trims leading and trailing whitespace from the string, up to but not
4305             * including the whitespace character specified by <code>c</code>.
4306             *
4307             * <p>
4308             * Examples:
4309             * </p>
4310             *
4311             * <p>
4312             * <pre>
4313             * <code>
4314             * trim(" \tHey\t ", '\t') returns "\tHey\t"
4315             * trim(" \t Hey \t ", '\t') returns "\t Hey \t"
4316             * </code>
4317             * </pre>
4318             * </p>
4319             *
4320             * @param  s the original string
4321             * @param  c the whitespace character to limit trimming
4322             * @return a string representing the original string with leading and
4323             *         trailing whitespace removed, up to but not including the
4324             *         whitespace character specified by <code>c</code>
4325             */
4326            public static String trim(String s, char c) {
4327                    return trim(s, new char[] {c});
4328            }
4329    
4330            /**
4331             * Trims leading and trailing whitespace from the string, up to but not
4332             * including the whitespace characters specified by <code>exceptions</code>.
4333             *
4334             * @param  s the original string
4335             * @param  exceptions the whitespace characters to limit trimming
4336             * @return a string representing the original string with leading and
4337             *         trailing whitespace removed, up to but not including the
4338             *         whitespace characters specified by <code>exceptions</code>
4339             */
4340            public static String trim(String s, char[] exceptions) {
4341                    if (s == null) {
4342                            return null;
4343                    }
4344    
4345                    if (s.length() == 0) {
4346                            return s;
4347                    }
4348    
4349                    if (ArrayUtil.isEmpty(exceptions)) {
4350                            return trim(s);
4351                    }
4352    
4353                    int len = s.length();
4354                    int x = len;
4355    
4356                    for (int i = 0; i < len; i++) {
4357                            char c = s.charAt(i);
4358    
4359                            if (!_isTrimable(c, exceptions)) {
4360                                    x = i;
4361    
4362                                    break;
4363                            }
4364                    }
4365    
4366                    if (x == len) {
4367                            return StringPool.BLANK;
4368                    }
4369    
4370                    int y = x + 1;
4371    
4372                    for (int i = len - 1; i > x; i--) {
4373                            char c = s.charAt(i);
4374    
4375                            if (!_isTrimable(c, exceptions)) {
4376                                    y = i + 1;
4377    
4378                                    break;
4379                            }
4380                    }
4381    
4382                    if ((x == 0) && (y == len)) {
4383                            return s;
4384                    }
4385                    else {
4386                            return s.substring(x, y);
4387                    }
4388            }
4389    
4390            /**
4391             * Trims all leading whitespace from the string.
4392             *
4393             * @param  s the original string
4394             * @return a string representing the original string with all leading
4395             *         whitespace removed
4396             */
4397            public static String trimLeading(String s) {
4398                    if (s == null) {
4399                            return null;
4400                    }
4401    
4402                    if (s.length() == 0) {
4403                            return s;
4404                    }
4405    
4406                    int len = s.length();
4407                    int x = len;
4408    
4409                    for (int i = 0; i < len; i++) {
4410                            char c = s.charAt(i);
4411    
4412                            if (!Character.isWhitespace(c)) {
4413                                    x = i;
4414    
4415                                    break;
4416                            }
4417                    }
4418    
4419                    if (x == len) {
4420                            return StringPool.BLANK;
4421                    }
4422                    else if (x == 0) {
4423                            return s;
4424                    }
4425                    else {
4426                            return s.substring(x);
4427                    }
4428            }
4429    
4430            /**
4431             * Trims leading whitespace from the string, up to but not including the
4432             * whitespace character specified by <code>c</code>.
4433             *
4434             * @param  s the original string
4435             * @param  c the whitespace character to limit trimming
4436             * @return a string representing the original string with leading whitespace
4437             *         removed, up to but not including the whitespace character
4438             *         specified by <code>c</code>
4439             */
4440            public static String trimLeading(String s, char c) {
4441                    return trimLeading(s, new char[] {c});
4442            }
4443    
4444            /**
4445             * Trims leading whitespace from the string, up to but not including the
4446             * whitespace characters specified by <code>exceptions</code>.
4447             *
4448             * @param  s the original string
4449             * @param  exceptions the whitespace characters to limit trimming
4450             * @return a string representing the original string with leading whitespace
4451             *         removed, up to but not including the whitespace characters
4452             *         specified by <code>exceptions</code>
4453             */
4454            public static String trimLeading(String s, char[] exceptions) {
4455                    if (s == null) {
4456                            return null;
4457                    }
4458    
4459                    if (s.length() == 0) {
4460                            return s;
4461                    }
4462    
4463                    if (ArrayUtil.isEmpty(exceptions)) {
4464                            return trimLeading(s);
4465                    }
4466    
4467                    int len = s.length();
4468                    int x = len;
4469    
4470                    for (int i = 0; i < len; i++) {
4471                            char c = s.charAt(i);
4472    
4473                            if (!_isTrimable(c, exceptions)) {
4474                                    x = i;
4475    
4476                                    break;
4477                            }
4478                    }
4479    
4480                    if (x == len) {
4481                            return StringPool.BLANK;
4482                    }
4483                    else if (x == 0) {
4484                            return s;
4485                    }
4486                    else {
4487                            return s.substring(x);
4488                    }
4489            }
4490    
4491            /**
4492             * Trims all trailing whitespace from the string.
4493             *
4494             * @param  s the original string
4495             * @return a string representing the original string with all trailing
4496             *         whitespace removed
4497             */
4498            public static String trimTrailing(String s) {
4499                    if (s == null) {
4500                            return null;
4501                    }
4502    
4503                    if (s.length() == 0) {
4504                            return s;
4505                    }
4506    
4507                    int len = s.length();
4508                    int x = 0;
4509    
4510                    for (int i = len - 1; i >= 0; i--) {
4511                            char c = s.charAt(i);
4512    
4513                            if (!Character.isWhitespace(c)) {
4514                                    x = i + 1;
4515    
4516                                    break;
4517                            }
4518                    }
4519    
4520                    if (x == 0) {
4521                            return StringPool.BLANK;
4522                    }
4523                    else if (x == len) {
4524                            return s;
4525                    }
4526                    else {
4527                            return s.substring(0, x);
4528                    }
4529            }
4530    
4531            /**
4532             * Trims trailing whitespace from the string, up to but not including the
4533             * whitespace character specified by <code>c</code>.
4534             *
4535             * @param  s the original string
4536             * @param  c the whitespace character to limit trimming
4537             * @return a string representing the original string with trailing
4538             *         whitespace removed, up to but not including the whitespace
4539             *         character specified by <code>c</code>
4540             */
4541            public static String trimTrailing(String s, char c) {
4542                    return trimTrailing(s, new char[] {c});
4543            }
4544    
4545            /**
4546             * Trims trailing whitespace from the string, up to but not including the
4547             * whitespace characters specified by <code>exceptions</code>.
4548             *
4549             * @param  s the original string
4550             * @param  exceptions the whitespace characters to limit trimming
4551             * @return a string representing the original string with trailing
4552             *         whitespace removed, up to but not including the whitespace
4553             *         characters specified by <code>exceptions</code>
4554             */
4555            public static String trimTrailing(String s, char[] exceptions) {
4556                    if (s == null) {
4557                            return null;
4558                    }
4559    
4560                    if (s.length() == 0) {
4561                            return s;
4562                    }
4563    
4564                    if (ArrayUtil.isEmpty(exceptions)) {
4565                            return trimTrailing(s);
4566                    }
4567    
4568                    int len = s.length();
4569                    int x = 0;
4570    
4571                    for (int i = len - 1; i >= 0; i--) {
4572                            char c = s.charAt(i);
4573    
4574                            if (!_isTrimable(c, exceptions)) {
4575                                    x = i + 1;
4576    
4577                                    break;
4578                            }
4579                    }
4580    
4581                    if (x == 0) {
4582                            return StringPool.BLANK;
4583                    }
4584                    else if (x == len) {
4585                            return s;
4586                    }
4587                    else {
4588                            return s.substring(0, x);
4589                    }
4590            }
4591    
4592            /**
4593             * Removes leading and trailing double and single quotation marks from the
4594             * string.
4595             *
4596             * @param  s the original string
4597             * @return a string representing the original string with leading and
4598             *         trailing double and single quotation marks removed, or the
4599             *         original string if the original string is a <code>null</code> or
4600             *         empty
4601             */
4602            public static String unquote(String s) {
4603                    if (Validator.isNull(s)) {
4604                            return s;
4605                    }
4606    
4607                    if ((s.charAt(0) == CharPool.APOSTROPHE) &&
4608                            (s.charAt(s.length() - 1) == CharPool.APOSTROPHE)) {
4609    
4610                            return s.substring(1, s.length() - 1);
4611                    }
4612                    else if ((s.charAt(0) == CharPool.QUOTE) &&
4613                                     (s.charAt(s.length() - 1) == CharPool.QUOTE)) {
4614    
4615                            return s.substring(1, s.length() - 1);
4616                    }
4617    
4618                    return s;
4619            }
4620    
4621            /**
4622             * Converts all of the characters in the string to upper case.
4623             *
4624             * @param  s the string to convert
4625             * @return the string, converted to upper-case, or <code>null</code> if the
4626             *         string is <code>null</code>
4627             * @see    String#toUpperCase()
4628             */
4629            public static String upperCase(String s) {
4630                    return toUpperCase(s);
4631            }
4632    
4633            /**
4634             * Converts the first character of the string to upper case.
4635             *
4636             * @param  s the string whose first character is to be converted
4637             * @return the string, with its first character converted to upper-case
4638             */
4639            public static String upperCaseFirstLetter(String s) {
4640                    char[] chars = s.toCharArray();
4641    
4642                    if ((chars[0] >= 97) && (chars[0] <= 122)) {
4643                            chars[0] = (char)(chars[0] - 32);
4644                    }
4645    
4646                    return new String(chars);
4647            }
4648    
4649            /**
4650             * Returns the string value of the object.
4651             *
4652             * @param  obj the object whose string value is to be returned
4653             * @return the string value of the object
4654             * @see    String#valueOf(Object obj)
4655             */
4656            public static String valueOf(Object obj) {
4657                    return String.valueOf(obj);
4658            }
4659    
4660            /**
4661             * Returns <code>true</code> if the string matches the wildcard pattern.
4662             *
4663             * <p>
4664             * For example, with the following initialized variables:
4665             * </p>
4666             *
4667             * <p>
4668             * <pre>
4669             * <code>
4670             * String s = "*master";
4671             * String wildcard = "/*m?st*";
4672             * char singleWildcardCharacter = '?';
4673             * char multipleWildcardCharacter = '*';
4674             * char escapeWildcardCharacter = '/';
4675             * boolean caseSensitive = false;
4676             * </code>
4677             * </pre>
4678             * </p>
4679             *
4680             * <p>
4681             * <code>wildcardMatches(s, wildcard, singleWildcardCharacter,
4682             * multipleWildcardCharacter, escapeWildcardCharacter, caseSensitive)</code>
4683             * returns <code>true</code>
4684             * </p>
4685             *
4686             * @param  s the string to be checked
4687             * @param  wildcard the wildcard pattern to match
4688             * @param  singleWildcardCharacter the char used to match exactly one
4689             *         character
4690             * @param  multipleWildcardCharacter the char used to match <code>0</code>
4691             *         or more characters
4692             * @param  escapeWildcardCharacter the char placed in front of a wildcard
4693             *         character to indicate that it should be interpreted as a regular
4694             *         character
4695             * @param  caseSensitive whether to use case sensitivity
4696             * @return <code>true</code> if the string matches the wildcard pattern;
4697             *         <code>false</code> otherwise
4698             */
4699            public static boolean wildcardMatches(
4700                    String s, String wildcard, char singleWildcardCharacter,
4701                    char multipleWildcardCharacter, char escapeWildcardCharacter,
4702                    boolean caseSensitive) {
4703    
4704                    if (!caseSensitive) {
4705                            s = toLowerCase(s);
4706                            wildcard = toLowerCase(wildcard);
4707                    }
4708    
4709                    // Update the wildcard, single whildcard character, and multiple
4710                    // wildcard character so that they no longer have escaped wildcard
4711                    // characters
4712    
4713                    int index = wildcard.indexOf(escapeWildcardCharacter);
4714    
4715                    if (index != -1) {
4716    
4717                            // Search for safe wildcard replacement
4718    
4719                            char newSingleWildcardCharacter = 0;
4720    
4721                            while (wildcard.indexOf(newSingleWildcardCharacter) != -1) {
4722                                    newSingleWildcardCharacter++;
4723                            }
4724    
4725                            char newMultipleWildcardCharacter =
4726                                    (char)(newSingleWildcardCharacter + 1);
4727    
4728                            while (wildcard.indexOf(newMultipleWildcardCharacter) != -1) {
4729                                    newMultipleWildcardCharacter++;
4730                            }
4731    
4732                            // Purify
4733    
4734                            StringBuilder sb = new StringBuilder(wildcard);
4735    
4736                            for (int i = 0; i < sb.length(); i++) {
4737                                    char c = sb.charAt(i);
4738    
4739                                    if (c == escapeWildcardCharacter) {
4740                                            sb.deleteCharAt(i);
4741                                    }
4742                                    else if (c == singleWildcardCharacter) {
4743                                            sb.setCharAt(i, newSingleWildcardCharacter);
4744                                    }
4745                                    else if (c == multipleWildcardCharacter) {
4746                                            sb.setCharAt(i, newMultipleWildcardCharacter);
4747                                    }
4748                            }
4749    
4750                            wildcard = sb.toString();
4751    
4752                            singleWildcardCharacter = newSingleWildcardCharacter;
4753                            multipleWildcardCharacter = newMultipleWildcardCharacter;
4754                    }
4755    
4756                    // Align head
4757    
4758                    for (index = 0; index < s.length(); index++) {
4759                            if (index >= wildcard.length()) {
4760                                    return false;
4761                            }
4762    
4763                            char c = wildcard.charAt(index);
4764    
4765                            if (c == multipleWildcardCharacter) {
4766                                    break;
4767                            }
4768    
4769                            if ((s.charAt(index) != c) && (c != singleWildcardCharacter)) {
4770                                    return false;
4771                            }
4772                    }
4773    
4774                    // Match body
4775    
4776                    int sIndex = index;
4777                    int wildcardIndex = index;
4778    
4779                    int matchPoint = 0;
4780                    int comparePoint = 0;
4781    
4782                    while (sIndex < s.length()) {
4783                            if (wildcardIndex == wildcard.length()) {
4784    
4785                                    // Wildcard exhausted before s
4786    
4787                                    return false;
4788                            }
4789    
4790                            char c = wildcard.charAt(wildcardIndex);
4791    
4792                            if (c == multipleWildcardCharacter) {
4793                                    if (++wildcardIndex == wildcard.length()) {
4794                                            return true;
4795                                    }
4796    
4797                                    matchPoint = wildcardIndex;
4798                                    comparePoint = sIndex + 1;
4799                            }
4800                            else if ((c == s.charAt(sIndex)) ||
4801                                             (c == singleWildcardCharacter)) {
4802    
4803                                    sIndex++;
4804                                    wildcardIndex++;
4805                            }
4806                            else {
4807                                    wildcardIndex = matchPoint;
4808                                    sIndex = comparePoint++;
4809                            }
4810                    }
4811    
4812                    // Match tail
4813    
4814                    while (wildcardIndex < wildcard.length()) {
4815                            if (wildcard.charAt(wildcardIndex) != multipleWildcardCharacter) {
4816                                    break;
4817                            }
4818    
4819                            wildcardIndex++;
4820                    }
4821    
4822                    if (wildcardIndex == wildcard.length()) {
4823                            return true;
4824                    }
4825                    else {
4826                            return false;
4827                    }
4828            }
4829    
4830            /**
4831             * Wraps the text when it exceeds the <code>80</code> column width limit,
4832             * using a {@link StringPool#NEW_LINE} to break each wrapped line.
4833             *
4834             * @param  text the text to wrap
4835             * @return the wrapped text following the column width limit, or
4836             *         <code>null</code> if the text is <code>null</code>
4837             */
4838            public static String wrap(String text) {
4839                    return wrap(text, 80, StringPool.NEW_LINE);
4840            }
4841    
4842            /**
4843             * Wraps the text when it exceeds the column width limit, using the line
4844             * separator to break each wrapped line.
4845             *
4846             * @param  text the text to wrap
4847             * @param  width the column width limit for the text
4848             * @param  lineSeparator the string to use in breaking each wrapped line
4849             * @return the wrapped text and line separators, following the column width
4850             *         limit, or <code>null</code> if the text is <code>null</code>
4851             */
4852            public static String wrap(String text, int width, String lineSeparator) {
4853                    try {
4854                            return _wrap(text, width, lineSeparator);
4855                    }
4856                    catch (IOException ioe) {
4857                            _log.error(ioe.getMessage());
4858    
4859                            return text;
4860                    }
4861            }
4862    
4863            protected static final char[] HEX_DIGITS = {
4864                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
4865                    'e', 'f'
4866            };
4867    
4868            /**
4869             * Returns <code>false</code> if the character is not whitespace or is equal
4870             * to any of the exception characters.
4871             *
4872             * @param  c the character whose trim-ability is to be determined
4873             * @param  exceptions the whitespace characters to exclude from trimming
4874             * @return <code>false</code> if the character is not whitespace or is equal
4875             *         to any of the exception characters; <code>true</code> otherwise
4876             */
4877            private static boolean _isTrimable(char c, char[] exceptions) {
4878                    for (char exception : exceptions) {
4879                            if (c == exception) {
4880                                    return false;
4881                            }
4882                    }
4883    
4884                    return Character.isWhitespace(c);
4885            }
4886    
4887            private static String _wrap(String text, int width, String lineSeparator)
4888                    throws IOException {
4889    
4890                    if (text == null) {
4891                            return null;
4892                    }
4893    
4894                    StringBundler sb = new StringBundler();
4895    
4896                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
4897                            new UnsyncStringReader(text));
4898    
4899                    String s = StringPool.BLANK;
4900    
4901                    while ((s = unsyncBufferedReader.readLine()) != null) {
4902                            if (s.length() == 0) {
4903                                    sb.append(lineSeparator);
4904    
4905                                    continue;
4906                            }
4907    
4908                            int lineLength = 0;
4909    
4910                            String[] tokens = s.split(StringPool.SPACE);
4911    
4912                            for (String token : tokens) {
4913                                    if ((lineLength + token.length() + 1) > width) {
4914                                            if (lineLength > 0) {
4915                                                    sb.append(lineSeparator);
4916                                            }
4917    
4918                                            if (token.length() > width) {
4919                                                    int pos = token.indexOf(CharPool.OPEN_PARENTHESIS);
4920    
4921                                                    if (pos != -1) {
4922                                                            sb.append(token.substring(0, pos + 1));
4923                                                            sb.append(lineSeparator);
4924    
4925                                                            token = token.substring(pos + 1);
4926    
4927                                                            sb.append(token);
4928    
4929                                                            lineLength = token.length();
4930                                                    }
4931                                                    else {
4932                                                            sb.append(token);
4933    
4934                                                            lineLength = token.length();
4935                                                    }
4936                                            }
4937                                            else {
4938                                                    sb.append(token);
4939    
4940                                                    lineLength = token.length();
4941                                            }
4942                                    }
4943                                    else {
4944                                            if (lineLength > 0) {
4945                                                    sb.append(StringPool.SPACE);
4946    
4947                                                    lineLength++;
4948                                            }
4949    
4950                                            sb.append(token);
4951    
4952                                            lineLength += token.length();
4953                                    }
4954                            }
4955    
4956                            sb.append(lineSeparator);
4957                    }
4958    
4959                    return sb.toString();
4960            }
4961    
4962            private static final char[] _RANDOM_STRING_CHAR_TABLE = {
4963                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
4964                    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
4965                    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4966                    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
4967                    'u', 'v', 'w', 'x', 'y', 'z'
4968            };
4969    
4970            private static final Log _log = LogFactoryUtil.getLog(StringUtil.class);
4971    
4972            private static final String[] _emptyStringArray = new String[0];
4973    
4974    }