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