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