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