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