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