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