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