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