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