001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.InputStreamReader;
025    
026    import java.net.URL;
027    
028    import java.util.ArrayList;
029    import java.util.Collection;
030    import java.util.Enumeration;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.StringTokenizer;
034    import java.util.regex.Matcher;
035    import java.util.regex.Pattern;
036    
037    /**
038     * The String utility class.
039     *
040     * @author Brian Wing Shun Chan
041     * @author Sandeep Soni
042     * @author Ganesh Ram
043     * @author Shuyang Zhou
044     */
045    public class StringUtil {
046    
047            /**
048             * Adds string <code>add</code> to string <code>s</code> resulting in a
049             * comma delimited list of strings, disallowing duplicate strings in the
050             * list.
051             *
052             * <p>
053             * The resulting string ends with a comma even if the original string does
054             * not.
055             * </p>
056             *
057             * @param  s the original string, representing a comma delimited list of
058             *         strings
059             * @param  add the string to add to the original, representing the string to
060             *         add to the list
061             * @return a string that represents the original string and the added string
062             *         separated by a comma, or <code>null</code> if the string to add
063             *         is <code>null</code>
064             */
065            public static String add(String s, String add) {
066                    return add(s, add, StringPool.COMMA);
067            }
068    
069            /**
070             * Adds string <code>add</code> to string <code>s</code> that represents a
071             * delimited list of strings, using a specified delimiter and disallowing
072             * duplicate words.
073             *
074             * <p>
075             * The returned string ends with the delimiter even if the original string
076             * does not.
077             * </p>
078             *
079             * @param  s the original string, representing a delimited list of strings
080             * @param  add the string to add to the original, representing the string to
081             *         add to the list
082             * @param  delimiter the delimiter used to separate strings in the list
083             * @return a string that represents the original string and the added string
084             *         separated by the delimiter, or <code>null</code> if the string to
085             *         add or the delimiter string is <code>null</code>
086             */
087            public static String add(String s, String add, String delimiter) {
088                    return add(s, add, delimiter, false);
089            }
090    
091            /**
092             * Adds string <code>add</code> to string <code>s</code> that represents a
093             * delimited list of strings, using a specified delimiter and optionally
094             * allowing duplicate words.
095             *
096             * <p>
097             * The returned string ends with the delimiter even if the original string
098             * does not.
099             * </p>
100             *
101             * @param  s the original string, representing a delimited list of strings
102             * @param  add the string to add to the original, representing the string to
103             *         add to the list
104             * @param  delimiter the delimiter used to separate strings in the list
105             * @param  allowDuplicates whether to allow duplicate strings
106             * @return a string that represents the original string and the added string
107             *         separated by the delimiter, or <code>null</code> if the string to
108             *         add or the delimiter string is <code>null</code>
109             */
110            public static String add(
111                    String s, String add, String delimiter, boolean allowDuplicates) {
112    
113                    if ((add == null) || (delimiter == null)) {
114                            return null;
115                    }
116    
117                    if (s == null) {
118                            s = StringPool.BLANK;
119                    }
120    
121                    if (allowDuplicates || !contains(s, add, delimiter)) {
122                            StringBundler sb = new StringBundler();
123    
124                            sb.append(s);
125    
126                            if (Validator.isNull(s) || s.endsWith(delimiter)) {
127                                    sb.append(add);
128                                    sb.append(delimiter);
129                            }
130                            else {
131                                    sb.append(delimiter);
132                                    sb.append(add);
133                                    sb.append(delimiter);
134                            }
135    
136                            s = sb.toString();
137                    }
138    
139                    return s;
140            }
141    
142            /**
143             * Returns the original string with an appended space followed by the string
144             * value of the suffix surrounded by parentheses.
145             *
146             * <p>
147             * If the original string ends with a numerical parenthetical suffix having
148             * an integer value equal to <code>suffix - 1</code>, then the existing
149             * parenthetical suffix is replaced by the new one.
150             * </p>
151             *
152             * <p>
153             * Examples:
154             * </p>
155             *
156             * <pre>
157             * <code>
158             * appendParentheticalSuffix("file", 0) returns "file (0)"
159             * appendParentheticalSuffix("file (0)", 0) returns "file (0) (0)"
160             * appendParentheticalSuffix("file (0)", 1) returns "file (1)"
161             * appendParentheticalSuffix("file (0)", 2) returns "file (0) (2)"
162             * </code>
163             * </p>
164             *
165             * @param  s the original string
166             * @param  suffix the suffix to be appended
167             * @return the resultant string whose characters equal those of the original
168             *         string, followed by a space, followed by the specified suffix
169             *         enclosed in parentheses, or, if the difference between the
170             *         provided suffix and the existing suffix is 1, the existing suffix
171             *         is incremented by 1
172             */
173            public static String appendParentheticalSuffix(String s, int suffix) {
174                    if (Pattern.matches(".* \\(" + String.valueOf(suffix - 1) + "\\)", s)) {
175                            int pos = s.lastIndexOf(" (");
176    
177                            s = s.substring(0, pos);
178                    }
179    
180                    return appendParentheticalSuffix(s, String.valueOf(suffix));
181            }
182    
183            /**
184             * Returns the original string with an appended space followed by the suffix
185             * surrounded by parentheses.
186             *
187             * <p>
188             * Example:
189             * </p>
190             *
191             * <pre>
192             * <code>
193             * appendParentheticalSuffix("Java", "EE") returns "Java (EE)"
194             * </code>
195             * </pre>
196             *
197             * @param  s the original string
198             * @param  suffix the suffix to be appended
199             * @return a string that represents the original string, followed by a
200             *         space, followed by the suffix enclosed in parentheses
201             */
202            public static String appendParentheticalSuffix(String s, String suffix) {
203                    StringBundler sb = new StringBundler(5);
204    
205                    sb.append(s);
206                    sb.append(StringPool.SPACE);
207                    sb.append(StringPool.OPEN_PARENTHESIS);
208                    sb.append(suffix);
209                    sb.append(StringPool.CLOSE_PARENTHESIS);
210    
211                    return sb.toString();
212            }
213    
214            /**
215             * Converts an array of bytes to a string representing the bytes in
216             * hexadecimal form.
217             *
218             * @param  bytes the array of bytes to be converted
219             * @return the string representing the bytes in hexadecimal form
220             */
221            public static String bytesToHexString(byte[] bytes) {
222                    StringBundler sb = new StringBundler(bytes.length * 2);
223    
224                    for (byte b : bytes) {
225                            String hex = Integer.toHexString(
226                                    0x0100 + (b & 0x00FF)).substring(1);
227    
228                            if (hex.length() < 2) {
229                                    sb.append("0");
230                            }
231    
232                            sb.append(hex);
233                    }
234    
235                    return sb.toString();
236            }
237    
238            /**
239             * Returns <code>true</code> if the string contains the text as a comma
240             * delimited list entry.
241             *
242             * <p>
243             * Example:
244             * </p>
245             *
246             * <pre>
247             * <code>
248             * contains("one,two,three", "two") returns true
249             * contains("one,two,three", "thr") returns false
250             * </code>
251             * </pre>
252             *
253             * @param  s the string in which to search
254             * @param  text the text to search for in the string
255             * @return <code>true</code> if the string contains the text as a comma
256             *         delimited list entry; <code>false</code> otherwise
257             */
258            public static boolean contains(String s, String text) {
259                    return contains(s, text, StringPool.COMMA);
260            }
261    
262            /**
263             * Returns <code>true</code> if the string contains the text as a delimited
264             * list entry.
265             *
266             * <p>
267             * Examples:
268             * </p>
269             *
270             * <pre>
271             * <code>
272             * contains("three...two...one", "two", "...") returns true
273             * contains("three...two...one", "thr", "...") returns false
274             * </code>
275             * </pre>
276             *
277             * @param  s the string in which to search
278             * @param  text the text to search for in the string
279             * @param  delimiter the delimiter
280             * @return <code>true</code> if the string contains the text as a delimited
281             *         list entry; <code>false</code> otherwise
282             */
283            public static boolean contains(String s, String text, String delimiter) {
284                    if ((s == null) || (text == null) || (delimiter == null)) {
285                            return false;
286                    }
287    
288                    if (!s.endsWith(delimiter)) {
289                            s = s.concat(delimiter);
290                    }
291    
292                    String dtd = delimiter.concat(text).concat(delimiter);
293    
294                    int pos = s.indexOf(dtd);
295    
296                    if (pos == -1) {
297                            String td = text.concat(delimiter);
298    
299                            if (s.startsWith(td)) {
300                                    return true;
301                            }
302    
303                            return false;
304                    }
305    
306                    return true;
307            }
308    
309            /**
310             * Returns the number of times the text appears in the string.
311             *
312             * @param  s the string in which to search
313             * @param  text the text to search for in the string
314             * @return the number of times the text appears in the string
315             */
316            public static int count(String s, String text) {
317                    if ((s == null) || (s.length() == 0) || (text == null) ||
318                            (text.length() == 0)) {
319    
320                            return 0;
321                    }
322    
323                    int count = 0;
324    
325                    int pos = s.indexOf(text);
326    
327                    while (pos != -1) {
328                            pos = s.indexOf(text, pos + text.length());
329    
330                            count++;
331                    }
332    
333                    return count;
334            }
335    
336            /**
337             * Returns <code>true</code> if the string ends with the specified
338             * character.
339             *
340             * @param  s the string in which to search
341             * @param  end the character to search for at the end of the string
342             * @return <code>true</code> if the string ends with the specified
343             *         character; <code>false</code> otherwise
344             */
345            public static boolean endsWith(String s, char end) {
346                    return endsWith(s, (new Character(end)).toString());
347            }
348    
349            /**
350             * Returns <code>true</code> if the string ends with the string
351             * <code>end</code>.
352             *
353             * @param  s the string in which to search
354             * @param  end the string to check for at the end of the string
355             * @return <code>true</code> if the string ends with the string
356             *         <code>end</code>; <code>false</code> otherwise
357             */
358            public static boolean endsWith(String s, String end) {
359                    if ((s == null) || (end == null)) {
360                            return false;
361                    }
362    
363                    if (end.length() > s.length()) {
364                            return false;
365                    }
366    
367                    String temp = s.substring(s.length() - end.length());
368    
369                    if (temp.equalsIgnoreCase(end)) {
370                            return true;
371                    }
372                    else {
373                            return false;
374                    }
375            }
376    
377            /**
378             * Returns the substring of each character instance in string <code>s</code>
379             * that is found in the character array <code>chars</code>. The substring of
380             * characters returned maintain their original order.
381             *
382             * @param  s the string from which to extract characters
383             * @param  chars the characters to extract from the string
384             * @return the substring of each character instance in string <code>s</code>
385             *         that is found in the character array <code>chars</code>, or an
386             *         empty string if the given string is <code>null</code>
387             */
388            public static String extract(String s, char[] chars) {
389                    if (s == null) {
390                            return StringPool.BLANK;
391                    }
392    
393                    StringBundler sb = new StringBundler();
394    
395                    for (char c1 : s.toCharArray()) {
396                            for (char c2 : chars) {
397                                    if (c1 == c2) {
398                                            sb.append(c1);
399    
400                                            break;
401                                    }
402                            }
403                    }
404    
405                    return sb.toString();
406            }
407    
408            /**
409             * Returns the substring of English characters from the string.
410             *
411             * @param  s the string from which to extract characters
412             * @return the substring of English characters from the string, or an empty
413             *         string if the given string is <code>null</code>
414             */
415            public static String extractChars(String s) {
416                    if (s == null) {
417                            return StringPool.BLANK;
418                    }
419    
420                    StringBundler sb = new StringBundler();
421    
422                    char[] chars = s.toCharArray();
423    
424                    for (char c : chars) {
425                            if (Validator.isChar(c)) {
426                                    sb.append(c);
427                            }
428                    }
429    
430                    return sb.toString();
431            }
432    
433            /**
434             * Returns a string consisting of all of the digits extracted from the
435             * string.
436             *
437             * @param  s the string from which to extract digits
438             * @return a string consisting of all of the digits extracted from the
439             *         string
440             */
441            public static String extractDigits(String s) {
442                    if (s == null) {
443                            return StringPool.BLANK;
444                    }
445    
446                    StringBundler sb = new StringBundler();
447    
448                    char[] chars = s.toCharArray();
449    
450                    for (char c : chars) {
451                            if (Validator.isDigit(c)) {
452                                    sb.append(c);
453                            }
454                    }
455    
456                    return sb.toString();
457            }
458    
459            /**
460             * Returns the substring of <code>s</code> up to but not including the first
461             * occurrence of the delimiter.
462             *
463             * @param  s the string from which to extract a substring
464             * @param  delimiter the character whose index in the string marks where to
465             *         end the substring
466             * @return the substring of <code>s</code> up to but not including the first
467             *         occurrence of the delimiter, <code>null</code> if the string is
468             *         <code>null</code> or the delimiter does not occur in the string
469             */
470            public static String extractFirst(String s, char delimiter) {
471                    if (s == null) {
472                            return null;
473                    }
474                    else {
475                            int index = s.indexOf(delimiter);
476    
477                            if (index < 0) {
478                                    return null;
479                            }
480                            else {
481                                    return s.substring(0, index);
482                            }
483                    }
484            }
485    
486            /**
487             * Returns the substring of <code>s</code> up to but not including the first
488             * occurrence of the delimiter.
489             *
490             * @param  s the string from which to extract a substring
491             * @param  delimiter the smaller string whose index in the larger string
492             *         marks where to end the substring
493             * @return the substring of <code>s</code> up to but not including the first
494             *         occurrence of the delimiter, <code>null</code> if the string is
495             *         <code>null</code> or the delimiter does not occur in the string
496             */
497            public static String extractFirst(String s, String delimiter) {
498                    if (s == null) {
499                            return null;
500                    }
501                    else {
502                            int index = s.indexOf(delimiter);
503    
504                            if (index < 0) {
505                                    return null;
506                            }
507                            else {
508                                    return s.substring(0, index);
509                            }
510                    }
511            }
512    
513            /**
514             * Returns the substring of <code>s</code> after but not including the last
515             * occurrence of the delimiter.
516             *
517             * @param  s the string from which to extract the substring
518             * @param  delimiter the character whose last index in the string marks
519             *         where to begin the substring
520             * @return the substring of <code>s</code> after but not including the last
521             *         occurrence of the delimiter, <code>null</code> if the string is
522             *         <code>null</code> or the delimiter does not occur in the string
523             */
524            public static String extractLast(String s, char delimiter) {
525                    if (s == null) {
526                            return null;
527                    }
528                    else {
529                            int index = s.lastIndexOf(delimiter);
530    
531                            if (index < 0) {
532                                    return null;
533                            }
534                            else {
535                                    return s.substring(index + 1);
536                            }
537                    }
538            }
539    
540            /**
541             * Returns the substring of <code>s</code> after but not including the last
542             * occurrence of the delimiter.
543             *
544             * @param  s the string from which to extract the substring
545             * @param  delimiter the string whose last index in the string marks where
546             *         to begin the substring
547             * @return the substring of <code>s</code> after but not including the last
548             *         occurrence of the delimiter, <code>null</code> if the string is
549             *         <code>null</code> or the delimiter does not occur in the string
550             */
551            public static String extractLast(String s, String delimiter) {
552                    if (s == null) {
553                            return null;
554                    }
555                    else {
556                            int index = s.lastIndexOf(delimiter);
557    
558                            if (index < 0) {
559                                    return null;
560                            }
561                            else {
562                                    return s.substring(index + delimiter.length());
563                            }
564                    }
565            }
566    
567            public static String extractLeadingDigits(String s) {
568                    if (s == null) {
569                            return StringPool.BLANK;
570                    }
571    
572                    StringBundler sb = new StringBundler();
573    
574                    char[] chars = s.toCharArray();
575    
576                    for (char c : chars) {
577                            if (Validator.isDigit(c)) {
578                                    sb.append(c);
579                            }
580                            else {
581                                    return sb.toString();
582                            }
583                    }
584    
585                    return sb.toString();
586            }
587    
588            /**
589             * @deprecated As of 6.1.0
590             */
591            public static String highlight(String s, String keywords) {
592                    return highlight(s, keywords, "<span class=\"highlight\">", "</span>");
593            }
594    
595            /**
596             * @deprecated As of 6.1.0
597             */
598            public static String highlight(
599                    String s, String keywords, String highlight1, String highlight2) {
600    
601                    if (Validator.isNull(s) || Validator.isNull(keywords)) {
602                            return s;
603                    }
604    
605                    Pattern pattern = Pattern.compile(
606                            Pattern.quote(keywords), Pattern.CASE_INSENSITIVE);
607    
608                    return _highlight(s, pattern, highlight1, highlight2);
609            }
610    
611            public static String highlight(String s, String[] queryTerms) {
612                    return highlight(
613                            s, queryTerms, "<span class=\"highlight\">", "</span>");
614            }
615    
616            public static String highlight(
617                    String s, String[] queryTerms, String highlight1, String highlight2) {
618    
619                    if (Validator.isNull(s) || Validator.isNull(queryTerms)) {
620                            return s;
621                    }
622    
623                    if (queryTerms.length == 0) {
624                            return StringPool.BLANK;
625                    }
626    
627                    StringBundler sb = new StringBundler(2 * queryTerms.length - 1);
628    
629                    for (int i = 0; i < queryTerms.length; i++) {
630                            sb.append(Pattern.quote(queryTerms[i].trim()));
631    
632                            if ((i + 1) < queryTerms.length) {
633                                    sb.append(StringPool.PIPE);
634                            }
635                    }
636    
637                    int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
638    
639                    Pattern pattern = Pattern.compile(sb.toString(), flags);
640    
641                    return _highlight(s, pattern, highlight1, highlight2);
642            }
643    
644            /**
645             * Inserts one string into the other at the specified offset index.
646             *
647             * @param  s the original string
648             * @param  insert the string to be inserted into the original string
649             * @param  offset the index of the original string where the insertion
650             *         should take place
651             * @return a string representing the original string with the other string
652             *         inserted at the specified offset index, or <code>null</code> if
653             *         the original string is <code>null</code>
654             */
655            public static String insert(String s, String insert, int offset) {
656                    if (s == null) {
657                            return null;
658                    }
659    
660                    if (insert == null) {
661                            return s;
662                    }
663    
664                    if (offset > s.length()) {
665                            return s.concat(insert);
666                    }
667                    else {
668                            String prefix = s.substring(0, offset);
669                            String postfix = s.substring(offset);
670    
671                            return prefix.concat(insert).concat(postfix);
672                    }
673            }
674    
675            /**
676             * Converts all of the characters in the string to lower case.
677             *
678             * @param  s the string to convert
679             * @return the string, converted to lowercase, or <code>null</code> if the
680             *         string is <code>null</code>
681             * @see    String#toLowerCase()
682             */
683            public static String lowerCase(String s) {
684                    if (s == null) {
685                            return null;
686                    }
687                    else {
688                            return s.toLowerCase();
689                    }
690            }
691    
692            public static void lowerCase(String... array) {
693                    if (array != null) {
694                            for (int i = 0; i < array.length; i++) {
695                                    array[i] = array[i].toLowerCase();
696                            }
697                    }
698            }
699    
700            /**
701             * Converts the first character of the string to lower case.
702             *
703             * @param  s the string whose first character is to be converted
704             * @return the string, with its first character converted to lower-case
705             */
706            public static String lowerCaseFirstLetter(String s) {
707                    char[] chars = s.toCharArray();
708    
709                    if ((chars[0] >= 65) && (chars[0] <= 90)) {
710                            chars[0] = (char)(chars[0] + 32);
711                    }
712    
713                    return new String(chars);
714            }
715    
716            /**
717             * Returns <code>true</code> if the specified pattern occurs at any position
718             * in the string.
719             *
720             * @param  s the string
721             * @param  pattern the pattern to search for in the string
722             * @return <code>true</code> if the specified pattern occurs at any position
723             *         in the string
724             */
725            public static boolean matches(String s, String pattern) {
726                    String[] array = pattern.split("\\*");
727    
728                    for (String element : array) {
729                            int pos = s.indexOf(element);
730    
731                            if (pos == -1) {
732                                    return false;
733                            }
734    
735                            s = s.substring(pos + element.length());
736                    }
737    
738                    return true;
739            }
740    
741            /**
742             * Returns <code>true</code> if the specified pattern occurs at any position
743             * in the string, ignoring case.
744             *
745             * @param  s the string
746             * @param  pattern the pattern to search for in the string
747             * @return <code>true</code> if the specified pattern occurs at any position
748             *         in the string
749             */
750            public static boolean matchesIgnoreCase(String s, String pattern) {
751                    return matches(lowerCase(s), lowerCase(pattern));
752            }
753    
754            /**
755             * Merges the elements of the boolean array into a string representing a
756             * comma delimited list of its values.
757             *
758             * @param  array the boolean values to merge
759             * @return a string representing a comma delimited list of the values of the
760             *         boolean array, an empty string if the array is empty, or
761             *         <code>null</code> if the array is <code>null</code>
762             */
763            public static String merge(boolean[] array) {
764                    return merge(array, StringPool.COMMA);
765            }
766    
767            /**
768             * Merges the elements of the boolean array into a string representing a
769             * delimited list of its values.
770             *
771             * @param  array the boolean values to merge
772             * @param  delimiter the delimiter
773             * @return a string representing a comma delimited list of the values of the
774             *         boolean array, an empty string if the array is empty, or
775             *         <code>null</code> if the array is <code>null</code>
776             */
777            public static String merge(boolean[] array, String delimiter) {
778                    if (array == null) {
779                            return null;
780                    }
781    
782                    if (array.length == 0) {
783                            return StringPool.BLANK;
784                    }
785    
786                    StringBundler sb = new StringBundler(2 * array.length - 1);
787    
788                    for (int i = 0; i < array.length; i++) {
789                            sb.append(String.valueOf(array[i]).trim());
790    
791                            if ((i + 1) != array.length) {
792                                    sb.append(delimiter);
793                            }
794                    }
795    
796                    return sb.toString();
797            }
798    
799            /**
800             * Merges the elements of the character array into a string representing a
801             * comma delimited list of its values.
802             *
803             * @param  array the characters to merge
804             * @return a string representing a comma delimited list of the values of the
805             *         character array, an empty string if the array is empty, or
806             *         <code>null</code> if the array is <code>null</code>
807             */
808            public static String merge(char[] array) {
809                    return merge(array, StringPool.COMMA);
810            }
811    
812            /**
813             * Merges the elements of the character array into a string representing a
814             * delimited list of its values.
815             *
816             * @param  array the characters to merge
817             * @param  delimiter the delimiter
818             * @return a string representing a delimited list of the values of the
819             *         character array, an empty string if the array is empty, or
820             *         <code>null</code> if the array is <code>null</code>
821             */
822            public static String merge(char[] array, String delimiter) {
823                    if (array == null) {
824                            return null;
825                    }
826    
827                    if (array.length == 0) {
828                            return StringPool.BLANK;
829                    }
830    
831                    StringBundler sb = new StringBundler(2 * array.length - 1);
832    
833                    for (int i = 0; i < array.length; i++) {
834                            sb.append(String.valueOf(array[i]).trim());
835    
836                            if ((i + 1) != array.length) {
837                                    sb.append(delimiter);
838                            }
839                    }
840    
841                    return sb.toString();
842            }
843    
844            public static String merge(Collection<?> col) {
845                    return merge(col, StringPool.COMMA);
846            }
847    
848            public static String merge(Collection<?> col, String delimiter) {
849                    if (col == null) {
850                            return null;
851                    }
852    
853                    return merge(col.toArray(new Object[col.size()]), delimiter);
854            }
855    
856            /**
857             * Merges the elements of an array of double-precision decimal numbers by
858             * returning a string representing a comma delimited list of its values.
859             *
860             * @param  array the doubles to merge
861             * @return a string representing a comma delimited list of the values of the
862             *         array of double-precision decimal numbers, an empty string if the
863             *         array is empty, or <code>null</code> if the array is
864             *         <code>null</code>
865             */
866            public static String merge(double[] array) {
867                    return merge(array, StringPool.COMMA);
868            }
869    
870            /**
871             * Merges the elements of an array of double-precision decimal numbers by
872             * returning a string representing a delimited list of its values.
873             *
874             * @param  array the doubles to merge
875             * @param  delimiter the delimiter
876             * @return a string representing a delimited list of the values of the array
877             *         of double-precision decimal numbers, an empty string if the array
878             *         is empty, or <code>null</code> if the array is <code>null</code>
879             */
880            public static String merge(double[] array, String delimiter) {
881                    if (array == null) {
882                            return null;
883                    }
884    
885                    if (array.length == 0) {
886                            return StringPool.BLANK;
887                    }
888    
889                    StringBundler sb = new StringBundler(2 * array.length - 1);
890    
891                    for (int i = 0; i < array.length; i++) {
892                            sb.append(String.valueOf(array[i]).trim());
893    
894                            if ((i + 1) != array.length) {
895                                    sb.append(delimiter);
896                            }
897                    }
898    
899                    return sb.toString();
900            }
901    
902            /**
903             * Merges the elements of an array of decimal numbers into a string
904             * representing a comma delimited list of its values.
905             *
906             * @param  array the floats to merge
907             * @return a string representing a comma delimited list of the values of the
908             *         array of decimal numbers, an empty string if the array is empty,
909             *         or <code>null</code> if the array is <code>null</code>
910             */
911            public static String merge(float[] array) {
912                    return merge(array, StringPool.COMMA);
913            }
914    
915            /**
916             * Merges the elements of an array of decimal numbers into a string
917             * representing a delimited list of its values.
918             *
919             * @param  array the floats to merge
920             * @param  delimiter the delimiter
921             * @return a string representing a delimited list of the values of the array
922             *         of decimal numbers, an empty string if the array is empty, or
923             *         <code>null</code> if the array is <code>null</code>
924             */
925            public static String merge(float[] array, String delimiter) {
926                    if (array == null) {
927                            return null;
928                    }
929    
930                    if (array.length == 0) {
931                            return StringPool.BLANK;
932                    }
933    
934                    StringBundler sb = new StringBundler(2 * array.length - 1);
935    
936                    for (int i = 0; i < array.length; i++) {
937                            sb.append(String.valueOf(array[i]).trim());
938    
939                            if ((i + 1) != array.length) {
940                                    sb.append(delimiter);
941                            }
942                    }
943    
944                    return sb.toString();
945            }
946    
947            /**
948             * Merges the elements of an array of integers into a string representing a
949             * comma delimited list of its values.
950             *
951             * @param  array the integers to merge
952             * @return a string representing a comma delimited list of the values of the
953             *         array of integers, an empty string if the array is empty, or
954             *         <code>null</code> if the array is <code>null</code>
955             */
956            public static String merge(int[] array) {
957                    return merge(array, StringPool.COMMA);
958            }
959    
960            /**
961             * Merges the elements of an array of integers into a string representing a
962             * delimited list of its values.
963             *
964             * @param  array the integers to merge
965             * @param  delimiter the delimiter
966             * @return a string representing a delimited list of the values of the array
967             *         of integers, an empty string if the array is empty, or
968             *         <code>null</code> if the array is <code>null</code>
969             */
970            public static String merge(int[] array, String delimiter) {
971                    if (array == null) {
972                            return null;
973                    }
974    
975                    if (array.length == 0) {
976                            return StringPool.BLANK;
977                    }
978    
979                    StringBundler sb = new StringBundler(2 * array.length - 1);
980    
981                    for (int i = 0; i < array.length; i++) {
982                            sb.append(String.valueOf(array[i]).trim());
983    
984                            if ((i + 1) != array.length) {
985                                    sb.append(delimiter);
986                            }
987                    }
988    
989                    return sb.toString();
990            }
991    
992            /**
993             * Merges the elements of an array of long integers by returning a string
994             * representing a comma delimited list of its values.
995             *
996             * @param  array the long integers to merge
997             * @return a string representing a comma delimited list of the values of the
998             *         array of long integers, an empty string if the array is empty, or
999             *         <code>null</code> if the array is <code>null</code>
1000             */
1001            public static String merge(long[] array) {
1002                    return merge(array, StringPool.COMMA);
1003            }
1004    
1005            /**
1006             * Merges the elements of an array of long integers by returning a string
1007             * representing a delimited list of its values.
1008             *
1009             * @param  array the long integers to merge
1010             * @param  delimiter the delimiter
1011             * @return a string representing a delimited list of the values of the array
1012             *         of long integers, an empty string if the array is empty, or
1013             *         <code>null</code> if the array is <code>null</code>
1014             */
1015            public static String merge(long[] array, String delimiter) {
1016                    if (array == null) {
1017                            return null;
1018                    }
1019    
1020                    if (array.length == 0) {
1021                            return StringPool.BLANK;
1022                    }
1023    
1024                    StringBundler sb = new StringBundler(2 * array.length - 1);
1025    
1026                    for (int i = 0; i < array.length; i++) {
1027                            sb.append(String.valueOf(array[i]).trim());
1028    
1029                            if ((i + 1) != array.length) {
1030                                    sb.append(delimiter);
1031                            }
1032                    }
1033    
1034                    return sb.toString();
1035            }
1036    
1037            /**
1038             * Merges the elements of an array of objects into a string representing a
1039             * comma delimited list of the objects.
1040             *
1041             * @param  array the objects to merge
1042             * @return a string representing a comma delimited list of the objects, an
1043             *         empty string if the array is empty, or <code>null</code> if the
1044             *         array is <code>null</code>
1045             */
1046            public static String merge(Object[] array) {
1047                    return merge(array, StringPool.COMMA);
1048            }
1049    
1050            /**
1051             * Merges the elements of an array of objects into a string representing a
1052             * delimited list of the objects.
1053             *
1054             * @param  array the objects to merge
1055             * @param  delimiter the delimiter
1056             * @return a string representing a delimited list of the objects, an empty
1057             *         string if the array is empty, or <code>null</code> if the array
1058             *         is <code>null</code>
1059             */
1060            public static String merge(Object[] array, String delimiter) {
1061                    if (array == null) {
1062                            return null;
1063                    }
1064    
1065                    if (array.length == 0) {
1066                            return StringPool.BLANK;
1067                    }
1068    
1069                    StringBundler sb = new StringBundler(2 * array.length - 1);
1070    
1071                    for (int i = 0; i < array.length; i++) {
1072                            sb.append(String.valueOf(array[i]).trim());
1073    
1074                            if ((i + 1) != array.length) {
1075                                    sb.append(delimiter);
1076                            }
1077                    }
1078    
1079                    return sb.toString();
1080            }
1081    
1082            /**
1083             * Merges the elements of an array of short integers by returning a string
1084             * representing a comma delimited list of its values.
1085             *
1086             * @param  array the short integers to merge
1087             * @return a string representing a comma delimited list of the values of the
1088             *         array of short integers, an empty string if the array is empty,
1089             *         or <code>null</code> if the array is <code>null</code>
1090             */
1091            public static String merge(short[] array) {
1092                    return merge(array, StringPool.COMMA);
1093            }
1094    
1095            /**
1096             * Merges the elements of an array of short integers by returning a string
1097             * representing a delimited list of its values.
1098             *
1099             * @param  array the short integers to merge
1100             * @param  delimiter the delimiter
1101             * @return a string representing a delimited list of the values of the array
1102             *         of short integers, an empty string if the array is empty, or
1103             *         <code>null</code> if the array is <code>null</code>
1104             */
1105            public static String merge(short[] array, String delimiter) {
1106                    if (array == null) {
1107                            return null;
1108                    }
1109    
1110                    if (array.length == 0) {
1111                            return StringPool.BLANK;
1112                    }
1113    
1114                    StringBundler sb = new StringBundler(2 * array.length - 1);
1115    
1116                    for (int i = 0; i < array.length; i++) {
1117                            sb.append(String.valueOf(array[i]).trim());
1118    
1119                            if ((i + 1) != array.length) {
1120                                    sb.append(delimiter);
1121                            }
1122                    }
1123    
1124                    return sb.toString();
1125            }
1126    
1127            /**
1128             * Returns the string enclosed by apostrophes.
1129             *
1130             * <p>
1131             * Example:
1132             * </p>
1133             *
1134             * <pre>
1135             * <code>
1136             * quote("Hello, World!") returns "'Hello, World!'"
1137             * </code>
1138             * </pre>
1139             *
1140             * @param  s the string to enclose in apostrophes
1141             * @return the string enclosed by apostrophes, or <code>null</code> if the
1142             *         string is <code>null</code>
1143             */
1144            public static String quote(String s) {
1145                    return quote(s, CharPool.APOSTROPHE);
1146            }
1147    
1148            /**
1149             * Returns the string enclosed by the quote character.
1150             *
1151             * <p>
1152             * Example:
1153             * </p>
1154             *
1155             * <pre>
1156             * <code>
1157             * quote("PATH", '%') returns "%PATH%"
1158             * </code>
1159             * </pre>
1160             *
1161             * @param  s the string to enclose in quotes
1162             * @param  quote the character to insert to insert to the beginning of and
1163             *         append to the end of the string
1164             * @return the string enclosed in the quote characters, or <code>null</code>
1165             *         if the string is <code>null</code>
1166             */
1167            public static String quote(String s, char quote) {
1168                    if (s == null) {
1169                            return null;
1170                    }
1171    
1172                    return quote(s, String.valueOf(quote));
1173            }
1174    
1175            /**
1176             * Returns the string enclosed by the quote strings.
1177             *
1178             * <p>
1179             * Example:
1180             * </p>
1181             *
1182             * <pre>
1183             * <code>
1184             * quote("WARNING", "!!!") returns "!!!WARNING!!!"
1185             * </code>
1186             * </pre>
1187             *
1188             * @param  s the string to enclose in quotes
1189             * @param  quote the quote string to insert to insert to the beginning of
1190             *         and append to the end of the string
1191             * @return the string enclosed in the quote strings, or <code>null</code> if
1192             *         the string is <code>null</code>
1193             */
1194            public static String quote(String s, String quote) {
1195                    if (s == null) {
1196                            return null;
1197                    }
1198    
1199                    return quote.concat(s).concat(quote);
1200            }
1201    
1202            /**
1203             * Pseudorandomly permutes the characters of the string.
1204             *
1205             * @param  s the string whose characters are to be randomized
1206             * @return a string of the same length as the string whose characters
1207             *         represent a pseudorandom permutation of the characters of the
1208             *         string
1209             */
1210            public static String randomize(String s) {
1211                    return Randomizer.getInstance().randomize(s);
1212            }
1213    
1214            public static String read(ClassLoader classLoader, String name)
1215                    throws IOException {
1216    
1217                    return read(classLoader, name, false);
1218            }
1219    
1220            public static String read(ClassLoader classLoader, String name, boolean all)
1221                    throws IOException {
1222    
1223                    if (all) {
1224                            StringBundler sb = new StringBundler();
1225    
1226                            Enumeration<URL> enu = classLoader.getResources(name);
1227    
1228                            while (enu.hasMoreElements()) {
1229                                    URL url = enu.nextElement();
1230    
1231                                    InputStream is = url.openStream();
1232    
1233                                    if (is == null) {
1234                                            throw new IOException(
1235                                                    "Unable to open resource at " + url.toString());
1236                                    }
1237    
1238                                    String s = read(is);
1239    
1240                                    if (s != null) {
1241                                            sb.append(s);
1242                                            sb.append(StringPool.NEW_LINE);
1243                                    }
1244                            }
1245    
1246                            return sb.toString().trim();
1247                    }
1248                    else {
1249                            InputStream is = classLoader.getResourceAsStream(name);
1250    
1251                            if (is == null) {
1252                                    throw new IOException(
1253                                            "Unable to open resource in class loader " + name);
1254                            }
1255    
1256                            String s = read(is);
1257    
1258                            return s;
1259                    }
1260            }
1261    
1262            public static String read(InputStream is) throws IOException {
1263                    StringBundler sb = new StringBundler();
1264    
1265                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
1266                            new InputStreamReader(is));
1267    
1268                    String line = null;
1269    
1270                    try {
1271                            while ((line = unsyncBufferedReader.readLine()) != null) {
1272                                    sb.append(line);
1273                                    sb.append(CharPool.NEW_LINE);
1274                            }
1275                    }
1276                    finally {
1277                            unsyncBufferedReader.close();
1278                    }
1279    
1280                    return sb.toString().trim();
1281            }
1282    
1283            public static void readLines(InputStream is, Collection<String> lines)
1284                    throws IOException {
1285    
1286                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
1287                            new InputStreamReader(is));
1288    
1289                    String line = null;
1290    
1291                    while ((line = unsyncBufferedReader.readLine()) != null) {
1292                            lines.add(line);
1293                    }
1294    
1295                    unsyncBufferedReader.close();
1296            }
1297    
1298            /**
1299             * Removes the <code>remove</code> string from string <code>s</code> that
1300             * represents a list of comma delimited strings.
1301             *
1302             * <p>
1303             * The resulting string ends with a comma even if the original string does
1304             * not.
1305             * </p>
1306             *
1307             * <p>
1308             * Examples:
1309             * </p>
1310             *
1311             * <pre>
1312             * <code>
1313             * remove("red,blue,green,yellow", "blue") returns "red,green,yellow,"
1314             * remove("blue", "blue") returns ""
1315             * remove("blue,", "blue") returns ""
1316             * </code>
1317             * </pre>
1318             *
1319             * @param  s the string representing the list of comma delimited strings
1320             * @param  remove the string to remove
1321             * @return a string representing the list of comma delimited strings with
1322             *         the <code>remove</code> string removed, or <code>null</code> if
1323             *         the original string, the string to remove, or the delimiter is
1324             *         <code>null</code>
1325             */
1326            public static String remove(String s, String remove) {
1327                    return remove(s, remove, StringPool.COMMA);
1328            }
1329    
1330            /**
1331             * Removes the <code>remove</code> string from string <code>s</code> that
1332             * represents a list of delimited strings.
1333             *
1334             * <p>
1335             * The resulting string ends with the delimiter even if the original string
1336             * does not.
1337             * </p>
1338             *
1339             * <p>
1340             * Examples:
1341             * </p>
1342             *
1343             * <pre>
1344             * <code>
1345             * remove("red;blue;green;yellow", "blue", ";") returns "red;green;yellow;"
1346             * remove("blue", "blue", ";") returns ""
1347             * remove("blue;", "blue", ";") returns ""
1348             * </code>
1349             * </pre>
1350             *
1351             * @param  s the string representing the list of delimited strings
1352             * @param  remove the string to remove
1353             * @param  delimiter the delimiter
1354             * @return a string representing the list of delimited strings with the
1355             *         <code>remove</code> string removed, or <code>null</code> if the
1356             *         original string, the string to remove, or the delimiter is
1357             *         <code>null</code>
1358             */
1359            public static String remove(String s, String remove, String delimiter) {
1360                    if ((s == null) || (remove == null) || (delimiter == null)) {
1361                            return null;
1362                    }
1363    
1364                    if (Validator.isNotNull(s) && !s.endsWith(delimiter)) {
1365                            s += delimiter;
1366                    }
1367    
1368                    String drd = delimiter.concat(remove).concat(delimiter);
1369    
1370                    String rd = remove.concat(delimiter);
1371    
1372                    while (contains(s, remove, delimiter)) {
1373                            int pos = s.indexOf(drd);
1374    
1375                            if (pos == -1) {
1376                                    if (s.startsWith(rd)) {
1377                                            int x = remove.length() + delimiter.length();
1378                                            int y = s.length();
1379    
1380                                            s = s.substring(x, y);
1381                                    }
1382                            }
1383                            else {
1384                                    int x = pos + remove.length() + delimiter.length();
1385                                    int y = s.length();
1386    
1387                                    String temp = s.substring(0, pos);
1388    
1389                                    s = temp.concat(s.substring(x, y));
1390                            }
1391                    }
1392    
1393                    return s;
1394            }
1395    
1396            /**
1397             * Replaces all occurrences of the character with the new character.
1398             *
1399             * @param  s the original string
1400             * @param  oldSub the character to be searched for and replaced in the
1401             *         original string
1402             * @param  newSub the character with which to replace the
1403             *         <code>oldSub</code> character
1404             * @return a string representing the original string with all occurrences of
1405             *         the <code>oldSub</code> character replaced with the
1406             *         <code>newSub</code> character, or <code>null</code> if the
1407             *         original string is <code>null</code>
1408             */
1409            public static String replace(String s, char oldSub, char newSub) {
1410                    if (s == null) {
1411                            return null;
1412                    }
1413    
1414                    return s.replace(oldSub, newSub);
1415            }
1416    
1417            /**
1418             * Replaces all occurrences of the character with the new string.
1419             *
1420             * @param  s the original string
1421             * @param  oldSub the character to be searched for and replaced in the
1422             *         original string
1423             * @param  newSub the string with which to replace the <code>oldSub</code>
1424             *         character
1425             * @return a string representing the original string with all occurrences of
1426             *         the <code>oldSub</code> character replaced with the string
1427             *         <code>newSub</code>, or <code>null</code> if the original string
1428             *         is <code>null</code>
1429             */
1430            public static String replace(String s, char oldSub, String newSub) {
1431                    if ((s == null) || (newSub == null)) {
1432                            return null;
1433                    }
1434    
1435                    // The number 5 is arbitrary and is used as extra padding to reduce
1436                    // buffer expansion
1437    
1438                    StringBundler sb = new StringBundler(s.length() + 5 * newSub.length());
1439    
1440                    char[] chars = s.toCharArray();
1441    
1442                    for (char c : chars) {
1443                            if (c == oldSub) {
1444                                    sb.append(newSub);
1445                            }
1446                            else {
1447                                    sb.append(c);
1448                            }
1449                    }
1450    
1451                    return sb.toString();
1452            }
1453    
1454            /**
1455             * Replaces all occurrences of the string with the new string.
1456             *
1457             * @param  s the original string
1458             * @param  oldSub the string to be searched for and replaced in the original
1459             *         string
1460             * @param  newSub the string with which to replace the <code>oldSub</code>
1461             *         string
1462             * @return a string representing the original string with all occurrences of
1463             *         the <code>oldSub</code> string replaced with the string
1464             *         <code>newSub</code>, or <code>null</code> if the original string
1465             *         is <code>null</code>
1466             */
1467            public static String replace(String s, String oldSub, String newSub) {
1468                    return replace(s, oldSub, newSub, 0);
1469            }
1470    
1471            /**
1472             * Replaces all occurrences of the string with the new string, starting from
1473             * the specified index.
1474             *
1475             * @param  s the original string
1476             * @param  oldSub the string to be searched for and replaced in the original
1477             *         string
1478             * @param  newSub the string with which to replace the <code>oldSub</code>
1479             *         string
1480             * @param  fromIndex the index of the original string from which to begin
1481             *         searching
1482             * @return a string representing the original string with all occurrences of
1483             *         the <code>oldSub</code> string occurring after the specified
1484             *         index replaced with the string <code>newSub</code>, or
1485             *         <code>null</code> if the original string is <code>null</code>
1486             */
1487            public static String replace(
1488                    String s, String oldSub, String newSub, int fromIndex) {
1489    
1490                    if (s == null) {
1491                            return null;
1492                    }
1493    
1494                    if ((oldSub == null) || oldSub.equals(StringPool.BLANK)) {
1495                            return s;
1496                    }
1497    
1498                    if (newSub == null) {
1499                            newSub = StringPool.BLANK;
1500                    }
1501    
1502                    int y = s.indexOf(oldSub, fromIndex);
1503    
1504                    if (y >= 0) {
1505                            StringBundler sb = new StringBundler();
1506    
1507                            int length = oldSub.length();
1508                            int x = 0;
1509    
1510                            while (x <= y) {
1511                                    sb.append(s.substring(x, y));
1512                                    sb.append(newSub);
1513    
1514                                    x = y + length;
1515                                    y = s.indexOf(oldSub, x);
1516                            }
1517    
1518                            sb.append(s.substring(x));
1519    
1520                            return sb.toString();
1521                    }
1522                    else {
1523                            return s;
1524                    }
1525            }
1526    
1527            public static String replace(
1528                    String s, String begin, String end, Map<String, String> values) {
1529    
1530                    StringBundler sb = replaceToStringBundler(s, begin, end, values);
1531    
1532                    return sb.toString();
1533            }
1534    
1535            /**
1536             * Replaces all occurrences of the elements of the string array with the
1537             * corresponding elements of the new string array.
1538             *
1539             * @param  s the original string
1540             * @param  oldSubs the strings to be searched for and replaced in the
1541             *         original string
1542             * @param  newSubs the strings with which to replace the
1543             *         <code>oldSubs</code> strings
1544             * @return a string representing the original string with all occurrences of
1545             *         the <code>oldSubs</code> strings replaced with the corresponding
1546             *         <code>newSubs</code> strings, or <code>null</code> if the
1547             *         original string, the <code>oldSubs</code> array, or the
1548             *         <code>newSubs</code> is <code>null</code>
1549             */
1550            public static String replace(String s, String[] oldSubs, String[] newSubs) {
1551                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
1552                            return null;
1553                    }
1554    
1555                    if (oldSubs.length != newSubs.length) {
1556                            return s;
1557                    }
1558    
1559                    for (int i = 0; i < oldSubs.length; i++) {
1560                            s = replace(s, oldSubs[i], newSubs[i]);
1561                    }
1562    
1563                    return s;
1564            }
1565    
1566            /**
1567             * Replaces all occurrences of the elements of the string array with the
1568             * corresponding elements of the new string array, optionally replacing only
1569             * substrings that are surrounded by word boundaries.
1570             *
1571             * <p>
1572             * Examples:
1573             * </p>
1574             *
1575             * <pre>
1576             * <code>
1577             * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGEYELLOW"
1578             * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorangeyellow"
1579             * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGE YELLOW"
1580             * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorange YELLOW"
1581             * replace("red orange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "RED ORANGE YELLOW"
1582             * replace("redorange.yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", * "YELLOW"}, true) returns "redorange.YELLOW"
1583             * </code>
1584             * </pre>
1585             *
1586             * @param  s the original string
1587             * @param  oldSubs the strings to be searched for and replaced in the
1588             *         original string
1589             * @param  newSubs the strings with which to replace the
1590             *         <code>oldSubs</code> strings
1591             * @param  exactMatch whether or not to replace only substrings of
1592             *         <code>s</code> that are surrounded by word boundaries
1593             * @return if <code>exactMatch</code> is <code>true</code>, a string
1594             *         representing the original string with all occurrences of the
1595             *         <code>oldSubs</code> strings that are surrounded by word
1596             *         boundaries replaced with the corresponding <code>newSubs</code>
1597             *         strings, or else a string representing the original string with
1598             *         all occurrences of the <code>oldSubs</code> strings replaced with
1599             *         the corresponding <code>newSubs</code> strings, or
1600             *         <code>null</code> if the original string, the
1601             *         <code>oldSubs</code> array, or the <code>newSubs</code is
1602             *         <code>null</code>
1603             */
1604            public static String replace(
1605                    String s, String[] oldSubs, String[] newSubs, boolean exactMatch) {
1606    
1607                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
1608                            return null;
1609                    }
1610    
1611                    if (oldSubs.length != newSubs.length) {
1612                            return s;
1613                    }
1614    
1615                    if (!exactMatch) {
1616                            return replace(s, oldSubs, newSubs);
1617                    }
1618    
1619                    for (int i = 0; i < oldSubs.length; i++) {
1620                            s = s.replaceAll("\\b" + oldSubs[i] + "\\b", newSubs[i]);
1621                    }
1622    
1623                    return s;
1624            }
1625    
1626            /**
1627             * Replaces the first occurrence of the character with the new character.
1628             *
1629             * @param  s the original string
1630             * @param  oldSub the character whose first occurrence in the original
1631             *         string is to be searched for and replaced
1632             * @param  newSub the character with which to replace the first occurrence
1633             *         of the <code>oldSub</code> character
1634             * @return a string representing the original string except with the first
1635             *         occurrence of the character <code>oldSub</code> replaced with the
1636             *         character <code>newSub</code>
1637             */
1638            public static String replaceFirst(String s, char oldSub, char newSub) {
1639                    if (s == null) {
1640                            return null;
1641                    }
1642    
1643                    return replaceFirst(s, String.valueOf(oldSub), String.valueOf(newSub));
1644            }
1645    
1646            /**
1647             * Replaces the first occurrence of the character with the new string.
1648             *
1649             * @param  s the original string
1650             * @param  oldSub the character whose first occurrence in the original
1651             *         string is to be searched for and replaced
1652             * @param  newSub the string with which to replace the first occurrence of
1653             *         the <code>oldSub</code> character
1654             * @return a string representing the original string except with the first
1655             *         occurrence of the character <code>oldSub</code> replaced with the
1656             *         string <code>newSub</code>
1657             */
1658            public static String replaceFirst(String s, char oldSub, String newSub) {
1659                    if ((s == null) || (newSub == null)) {
1660                            return null;
1661                    }
1662    
1663                    return replaceFirst(s, String.valueOf(oldSub), newSub);
1664            }
1665    
1666            /**
1667             * Replaces the first occurrence of the string with the new string.
1668             *
1669             * @param  s the original string
1670             * @param  oldSub the string whose first occurrence in the original string
1671             *         is to be searched for and replaced
1672             * @param  newSub the string with which to replace the first occurrence of
1673             *         the <code>oldSub</code> string
1674             * @return a string representing the original string except with the first
1675             *         occurrence of the string <code>oldSub</code> replaced with the
1676             *         string <code>newSub</code>
1677             */
1678            public static String replaceFirst(String s, String oldSub, String newSub) {
1679                    if ((s == null) || (oldSub == null) || (newSub == null)) {
1680                            return null;
1681                    }
1682    
1683                    if (oldSub.equals(newSub)) {
1684                            return s;
1685                    }
1686    
1687                    int y = s.indexOf(oldSub);
1688    
1689                    if (y >= 0) {
1690                            return s.substring(0, y).concat(newSub).concat(
1691                                    s.substring(y + oldSub.length()));
1692                    }
1693                    else {
1694                            return s;
1695                    }
1696            }
1697    
1698            /**
1699             * Replaces the first occurrences of the elements of the string array with
1700             * the corresponding elements of the new string array.
1701             *
1702             * @param  s the original string
1703             * @param  oldSubs the strings whose first occurrences are to be searched
1704             *         for and replaced in the original string
1705             * @param  newSubs the strings with which to replace the first occurrences
1706             *         of the <code>oldSubs</code> strings
1707             * @return a string representing the original string with the first
1708             *         occurrences of the <code>oldSubs</code> strings replaced with the
1709             *         corresponding <code>newSubs</code> strings, or <code>null</code>
1710             *         if the original string, the <code>oldSubs</code> array, or the
1711             *         <code>newSubs</code is <code>null</code>
1712             */
1713            public static String replaceFirst(
1714                    String s, String[] oldSubs, String[] newSubs) {
1715    
1716                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
1717                            return null;
1718                    }
1719    
1720                    if (oldSubs.length != newSubs.length) {
1721                            return s;
1722                    }
1723    
1724                    for (int i = 0; i < oldSubs.length; i++) {
1725                            s = replaceFirst(s, oldSubs[i], newSubs[i]);
1726                    }
1727    
1728                    return s;
1729            }
1730    
1731            /**
1732             * Replaces the last occurrence of the character with the new character.
1733             *
1734             * @param  s the original string
1735             * @param  oldSub the character whose last occurrence in the original string
1736             *         is to be searched for and replaced
1737             * @param  newSub the character with which to replace the last occurrence of
1738             *         the <code>oldSub</code> character
1739             * @return a string representing the original string except with the first
1740             *         occurrence of the character <code>oldSub</code> replaced with the
1741             *         character <code>newSub</code>
1742             */
1743            public static String replaceLast(String s, char oldSub, char newSub) {
1744                    if (s == null) {
1745                            return null;
1746                    }
1747    
1748                    return replaceLast(s, String.valueOf(oldSub), String.valueOf(newSub));
1749            }
1750    
1751            /**
1752             * Replaces the last occurrence of the character with the new string.
1753             *
1754             * @param  s the original string
1755             * @param  oldSub the character whose last occurrence in the original string
1756             *         is to be searched for and replaced
1757             * @param  newSub the string with which to replace the last occurrence of
1758             *         the <code>oldSub</code> character
1759             * @return a string representing the original string except with the last
1760             *         occurrence of the character <code>oldSub</code> replaced with the
1761             *         string <code>newSub</code>
1762             */
1763            public static String replaceLast(String s, char oldSub, String newSub) {
1764                    if ((s == null) || (newSub == null)) {
1765                            return null;
1766                    }
1767    
1768                    return replaceLast(s, String.valueOf(oldSub), newSub);
1769            }
1770    
1771            /**
1772             * Replaces the last occurrence of the string <code>oldSub</code> in the
1773             * string <code>s</code> with the string <code>newSub</code>.
1774             *
1775             * @param  s the original string
1776             * @param  oldSub the string whose last occurrence in the original string is
1777             *         to be searched for and replaced
1778             * @param  newSub the string with which to replace the last occurrence of
1779             *         the <code>oldSub</code> string
1780             * @return a string representing the original string except with the last
1781             *         occurrence of the string <code>oldSub</code> replaced with the
1782             *         string <code>newSub</code>
1783             */
1784            public static String replaceLast(String s, String oldSub, String newSub) {
1785                    if ((s == null) || (oldSub == null) || (newSub == null)) {
1786                            return null;
1787                    }
1788    
1789                    if (oldSub.equals(newSub)) {
1790                            return s;
1791                    }
1792    
1793                    int y = s.lastIndexOf(oldSub);
1794    
1795                    if (y >= 0) {
1796                            return s.substring(0, y).concat(newSub).concat(
1797                                    s.substring(y + oldSub.length()));
1798                    }
1799                    else {
1800                            return s;
1801                    }
1802            }
1803    
1804            /**
1805             * Replaces the last occurrences of the elements of the string array with
1806             * the corresponding elements of the new string array.
1807             *
1808             * @param  s the original string
1809             * @param  oldSubs the strings whose last occurrences are to be searched for
1810             *         and replaced in the original string
1811             * @param  newSubs the strings with which to replace the last occurrences of
1812             *         the <code>oldSubs</code> strings
1813             * @return a string representing the original string with the last
1814             *         occurrences of the <code>oldSubs</code> strings replaced with the
1815             *         corresponding <code>newSubs</code> strings, or <code>null</code>
1816             *         if the original string, the <code>oldSubs</code> array, or the
1817             *         <code>newSubs</code is <code>null</code>
1818             */
1819            public static String replaceLast(
1820                    String s, String[] oldSubs, String[] newSubs) {
1821    
1822                    if ((s == null) || (oldSubs == null) || (newSubs == null)) {
1823                            return null;
1824                    }
1825    
1826                    if (oldSubs.length != newSubs.length) {
1827                            return s;
1828                    }
1829    
1830                    for (int i = 0; i < oldSubs.length; i++) {
1831                            s = replaceLast(s, oldSubs[i], newSubs[i]);
1832                    }
1833    
1834                    return s;
1835            }
1836    
1837            public static StringBundler replaceToStringBundler(
1838                    String s, String begin, String end, Map<String, String> values) {
1839    
1840                    if ((s == null) || (begin == null) || (end == null) ||
1841                            (values == null) || (values.size() == 0)) {
1842    
1843                            return new StringBundler(s);
1844                    }
1845    
1846                    StringBundler sb = new StringBundler(values.size() * 2 + 1);
1847    
1848                    int pos = 0;
1849    
1850                    while (true) {
1851                            int x = s.indexOf(begin, pos);
1852                            int y = s.indexOf(end, x + begin.length());
1853    
1854                            if ((x == -1) || (y == -1)) {
1855                                    sb.append(s.substring(pos));
1856    
1857                                    break;
1858                            }
1859                            else {
1860                                    sb.append(s.substring(pos, x));
1861    
1862                                    String oldValue = s.substring(x + begin.length(), y);
1863    
1864                                    String newValue = values.get(oldValue);
1865    
1866                                    if (newValue == null) {
1867                                            newValue = oldValue;
1868                                    }
1869    
1870                                    sb.append(newValue);
1871    
1872                                    pos = y + end.length();
1873                            }
1874                    }
1875    
1876                    return sb;
1877            }
1878    
1879            public static StringBundler replaceWithStringBundler(
1880                    String s, String begin, String end, Map<String, StringBundler> values) {
1881    
1882                    if ((s == null) || (begin == null) || (end == null) ||
1883                            (values == null) || (values.size() == 0)) {
1884    
1885                            return new StringBundler(s);
1886                    }
1887    
1888                    int size = values.size() + 1;
1889    
1890                    for (StringBundler valueSB : values.values()) {
1891                            size += valueSB.index();
1892                    }
1893    
1894                    StringBundler sb = new StringBundler(size);
1895    
1896                    int pos = 0;
1897    
1898                    while (true) {
1899                            int x = s.indexOf(begin, pos);
1900                            int y = s.indexOf(end, x + begin.length());
1901    
1902                            if ((x == -1) || (y == -1)) {
1903                                    sb.append(s.substring(pos));
1904    
1905                                    break;
1906                            }
1907                            else {
1908                                    sb.append(s.substring(pos, x));
1909    
1910                                    String oldValue = s.substring(x + begin.length(), y);
1911    
1912                                    StringBundler newValue = values.get(oldValue);
1913    
1914                                    if (newValue == null) {
1915                                            sb.append(oldValue);
1916                                    }
1917                                    else {
1918                                            sb.append(newValue);
1919                                    }
1920    
1921                                    pos = y + end.length();
1922                            }
1923                    }
1924    
1925                    return sb;
1926            }
1927    
1928            /**
1929             * Reverses the order of the characters of the string.
1930             *
1931             * @param  s the original string
1932             * @return a string representing the original string with characters in
1933             *         reverse order
1934             */
1935            public static String reverse(String s) {
1936                    if (s == null) {
1937                            return null;
1938                    }
1939    
1940                    char[] chars = s.toCharArray();
1941                    char[] reverse = new char[chars.length];
1942    
1943                    for (int i = 0; i < chars.length; i++) {
1944                            reverse[i] = chars[chars.length - i - 1];
1945                    }
1946    
1947                    return new String(reverse);
1948            }
1949    
1950            /**
1951             * Replaces all double slashes of the string with single slashes.
1952             *
1953             * <p>
1954             * Example:
1955             * </p>
1956             *
1957             * <pre>
1958             * <code>
1959             * safePath("http://www.liferay.com") returns "http:/www.liferay.com"
1960             * </code>
1961             * </pre>
1962             *
1963             * @param  path the original string
1964             * @return a string representing the original string with all double slashes
1965             *         replaced with single slashes
1966             */
1967            public static String safePath(String path) {
1968                    return replace(path, StringPool.DOUBLE_SLASH, StringPool.SLASH);
1969            }
1970    
1971            /**
1972             * Returns a string representing the original string appended with suffix
1973             * "..." and then shortened to 20 characters.
1974             *
1975             * <p>
1976             * The suffix is only added if the original string exceeds 20 characters. If
1977             * the original string exceeds 20 characters and it contains whitespace, the
1978             * string is shortened at the first whitespace character.
1979             * </p>
1980             *
1981             * <p>
1982             * Examples:
1983             * </p>
1984             *
1985             * <pre>
1986             * <code>
1987             * shorten("12345678901234567890xyz") returns "12345678901234567..."
1988             * shorten("1 345678901234567890xyz") returns "1..."
1989             * shorten(" 2345678901234567890xyz") returns "..."
1990             * shorten("12345678901234567890") returns "12345678901234567890"
1991             * shorten(" 2345678901234567890") returns " 2345678901234567890"
1992             * </code>
1993             * </pre>
1994             *
1995             * @param  s the original string
1996             * @return a string representing the original string shortened to 20
1997             *         characters, with suffix "..." appended to it
1998             */
1999            public static String shorten(String s) {
2000                    return shorten(s, 20);
2001            }
2002    
2003            /**
2004             * Returns a string representing the original string appended with suffix
2005             * "..." and then shortened to the specified length.
2006             *
2007             * <p>
2008             * The suffix is only added if the original string exceeds the specified
2009             * length. If the original string exceeds the specified length and it
2010             * contains whitespace, the string is shortened at the first whitespace
2011             * character.
2012             * </p>
2013             *
2014             * <p>
2015             * Examples:
2016             * </p>
2017             *
2018             * <pre>
2019             * <code>
2020             * shorten("123456789", 8) returns "12345..."
2021             * shorten("1 3456789", 8) returns "1..."
2022             * shorten(" 23456789", 8) returns "..."
2023             * shorten("12345678", 8) returns "12345678"
2024             * shorten(" 1234567", 8) returns " 1234567"
2025             * </code>
2026             * </pre>
2027             *
2028             * @param  s the original string
2029             * @param  length the number of characters to limit from the original string
2030             * @return a string representing the original string shortened to the
2031             *         specified length, with suffix "..." appended to it
2032             */
2033            public static String shorten(String s, int length) {
2034                    return shorten(s, length, "...");
2035            }
2036    
2037            /**
2038             * Returns a string representing the original string appended with the
2039             * specified suffix and then shortened to the specified length.
2040             *
2041             * <p>
2042             * The suffix is only added if the original string exceeds the specified
2043             * length. If the original string exceeds the specified length and it
2044             * contains whitespace, the string is shortened at the first whitespace
2045             * character.
2046             * </p>
2047             *
2048             * <p>
2049             * Examples:
2050             * </p>
2051             *
2052             * <pre>
2053             * <code>
2054             * shorten("12345678901234", 13, "... etc.") returns "12345... etc."
2055             * shorten("1 345678901234", 13, "... etc.") returns "1... etc."
2056             * shorten(" 2345678901234", 13, "... etc.") returns "... etc."
2057             * shorten("1234567890123", 13, "... etc.") returns "1234567890123"
2058             * shorten(" 123456789012", 13, "... etc.") returns " 123456789012"
2059             * </code>
2060             * </pre>
2061             *
2062             * @param  s the original string
2063             * @param  length the number of characters to limit from the original string
2064             * @param  suffix the suffix to append
2065             * @return a string representing the original string shortened to the
2066             *         specified length, with the specified suffix appended to it
2067             */
2068            public static String shorten(String s, int length, String suffix) {
2069                    if ((s == null) || (suffix == null)) {
2070                            return null;
2071                    }
2072    
2073                    if (s.length() <= length) {
2074                            return s;
2075                    }
2076    
2077                    if (length < suffix.length()) {
2078                            return s.substring(0, length);
2079                    }
2080    
2081                    int curLength = length;
2082    
2083                    for (int j = (curLength - suffix.length()); j >= 0; j--) {
2084                            if (Character.isWhitespace(s.charAt(j))) {
2085                                    curLength = j;
2086    
2087                                    break;
2088                            }
2089                    }
2090    
2091                    if (curLength == length) {
2092                            curLength = length - suffix.length();
2093                    }
2094    
2095                    String temp = s.substring(0, curLength);
2096    
2097                    return temp.concat(suffix);
2098            }
2099    
2100            /**
2101             * Returns a string representing the original string appended with the
2102             * specified suffix and then shortened to 20 characters.
2103             *
2104             * <p>
2105             * The suffix is only added if the original string exceeds 20 characters. If
2106             * the original string exceeds 20 characters and it contains whitespace, the
2107             * string is shortened at the first whitespace character.
2108             * </p>
2109             *
2110             * <p>
2111             * Examples:
2112             * </p>
2113             *
2114             * <pre>
2115             * <code>
2116             * shorten("12345678901234567890xyz", "... etc.") returns "123456789012... etc."
2117             * shorten("1 345678901234567890xyz", "... etc.") returns "1... etc."
2118             * shorten(" 2345678901234567890xyz", "... etc.") returns "... etc."
2119             * shorten("12345678901234567890", "... etc.") returns "12345678901234567890"
2120             * shorten(" 2345678901234567890", "... etc.") returns " 2345678901234567890"
2121             * </code>
2122             * </pre>
2123             *
2124             * @param  s the original string
2125             * @param  suffix the suffix to append
2126             * @return a string representing the original string shortened to 20
2127             *         characters, with the specified suffix appended to it
2128             */
2129            public static String shorten(String s, String suffix) {
2130                    return shorten(s, 20, suffix);
2131            }
2132    
2133            /**
2134             * Splits string <code>s</code> around comma characters.
2135             *
2136             * <p>
2137             * Example:
2138             * </p>
2139             *
2140             * <pre>
2141             * <code>
2142             * split("Alice,Bob,Charlie") returns {"Alice", "Bob", "Charlie"}
2143             * split("Alice, Bob, Charlie") returns {"Alice", " Bob", " Charlie"}
2144             * </code>
2145             * </pre>
2146             *
2147             * @param  s the string to split
2148             * @return the array of strings resulting from splitting string
2149             *         <code>s</code> around comma characters, or an empty string array
2150             *         if <code>s</code> is <code>null</code> or <code>s</code> is empty
2151             */
2152            public static String[] split(String s) {
2153                    return split(s, CharPool.COMMA);
2154            }
2155    
2156            /**
2157             * Splits the string <code>s</code> around comma characters returning the
2158             * boolean values of the substrings.
2159             *
2160             * @param  s the string to split
2161             * @param  x the default value to use for a substring in case an exception
2162             *         occurs in getting the boolean value for that substring
2163             * @return the array of boolean values resulting from splitting string
2164             *         <code>s</code> around comma characters, or an empty array if
2165             *         <code>s</code> is <code>null</code>
2166             */
2167            public static boolean[] split(String s, boolean x) {
2168                    return split(s, StringPool.COMMA, x);
2169            }
2170    
2171            /**
2172             * Splits the string <code>s</code> around the specified delimiter.
2173             *
2174             * <p>
2175             * Example:
2176             * </p>
2177             *
2178             * <pre>
2179             * <code>
2180             * splitLines("First;Second;Third", ';') returns {"First","Second","Third"}
2181             * </code>
2182             * </pre>
2183             *
2184             * @param  s the string to split
2185             * @param  delimiter the delimiter
2186             * @return the array of strings resulting from splitting string
2187             *         <code>s</code> around the specified delimiter character, or an
2188             *         empty string array if <code>s</code> is <code>null</code> or if
2189             *         <code>s</code> is empty
2190             */
2191            public static String[] split(String s, char delimiter) {
2192                    if (Validator.isNull(s)) {
2193                            return _emptyStringArray;
2194                    }
2195    
2196                    s = s.trim();
2197    
2198                    if (s.length() == 0) {
2199                            return _emptyStringArray;
2200                    }
2201    
2202                    if ((delimiter == CharPool.RETURN) ||
2203                            (delimiter == CharPool.NEW_LINE)) {
2204    
2205                            return splitLines(s);
2206                    }
2207    
2208                    List<String> nodeValues = new ArrayList<String>();
2209    
2210                    int offset = 0;
2211                    int pos = s.indexOf(delimiter, offset);
2212    
2213                    while (pos != -1) {
2214                            nodeValues.add(s.substring(offset, pos));
2215    
2216                            offset = pos + 1;
2217                            pos = s.indexOf(delimiter, offset);
2218                    }
2219    
2220                    if (offset < s.length()) {
2221                            nodeValues.add(s.substring(offset));
2222                    }
2223    
2224                    return nodeValues.toArray(new String[nodeValues.size()]);
2225            }
2226    
2227            /**
2228             * Splits the string <code>s</code> around comma characters returning the
2229             * double-precision decimal values of the substrings.
2230             *
2231             * @param  s the string to split
2232             * @param  x the default value to use for a substring in case an exception
2233             *         occurs in getting the double-precision decimal value for that
2234             *         substring
2235             * @return the array of double-precision decimal values resulting from
2236             *         splitting string <code>s</code> around comma characters, or an
2237             *         empty array if <code>s</code> is <code>null</code>
2238             */
2239            public static double[] split(String s, double x) {
2240                    return split(s, StringPool.COMMA, x);
2241            }
2242    
2243            /**
2244             * Splits the string <code>s</code> around comma characters returning the
2245             * decimal values of the substrings.
2246             *
2247             * @param  s the string to split
2248             * @param  x the default value to use for a substring in case an exception
2249             *         occurs in getting the decimal value for that substring
2250             * @return the array of decimal values resulting from splitting string
2251             *         <code>s</code> around comma characters, or an empty array if
2252             *         <code>s</code> is <code>null</code>
2253             */
2254            public static float[] split(String s, float x) {
2255                    return split(s, StringPool.COMMA, x);
2256            }
2257    
2258            /**
2259             * Splits the string <code>s</code> around comma characters returning the
2260             * integer values of the substrings.
2261             *
2262             * @param  s the string to split
2263             * @param  x the default value to use for a substring in case an exception
2264             *         occurs in getting the integer value for that substring
2265             * @return the array of integer values resulting from splitting string
2266             *         <code>s</code> around comma characters, or an empty array if
2267             *         <code>s</code> is <code>null</code>
2268             */
2269            public static int[] split(String s, int x) {
2270                    return split(s, StringPool.COMMA, x);
2271            }
2272    
2273            /**
2274             * Splits the string <code>s</code> around comma characters returning the
2275             * long integer values of the substrings.
2276             *
2277             * @param  s the string to split
2278             * @param  x the default value to use for a substring in case an exception
2279             *         occurs in getting the long integer value for that substring
2280             * @return the array of long integer values resulting from splitting string
2281             *         <code>s</code> around comma characters, or an empty array if
2282             *         <code>s</code> is <code>null</code>
2283             */
2284            public static long[] split(String s, long x) {
2285                    return split(s, StringPool.COMMA, x);
2286            }
2287    
2288            /**
2289             * Splits the string <code>s</code> around comma characters returning the
2290             * short integer values of the substrings.
2291             *
2292             * @param  s the string to split
2293             * @param  x the default value to use for a substring in case an exception
2294             *         occurs in getting the short integer value for that substring
2295             * @return the array of short integer values resulting from splitting string
2296             *         <code>s</code> around comma characters, or an empty array if
2297             *         <code>s</code> is <code>null</code>
2298             */
2299            public static short[] split(String s, short x) {
2300                    return split(s, StringPool.COMMA, x);
2301            }
2302    
2303            /**
2304             * Splits the string <code>s</code> around the specified delimiter string.
2305             *
2306             * <p>
2307             * Example:
2308             * </p>
2309             *
2310             * <pre>
2311             * <code>
2312             * splitLines("oneandtwoandthreeandfour", "and") returns {"one","two","three","four"}
2313             * </code>
2314             * </pre>
2315             *
2316             * @param  s the string to split
2317             * @param  delimiter the delimiter
2318             * @return the array of strings resulting from splitting string
2319             *         <code>s</code> around the specified delimiter string, or an empty
2320             *         string array if <code>s</code> is <code>null</code> or equals the
2321             *         delimiter
2322             */
2323            public static String[] split(String s, String delimiter) {
2324                    if (Validator.isNull(s) || (delimiter == null) ||
2325                            delimiter.equals(StringPool.BLANK)) {
2326    
2327                            return _emptyStringArray;
2328                    }
2329    
2330                    s = s.trim();
2331    
2332                    if (s.equals(delimiter)) {
2333                            return _emptyStringArray;
2334                    }
2335    
2336                    if (delimiter.length() == 1) {
2337                            return split(s, delimiter.charAt(0));
2338                    }
2339    
2340                    List<String> nodeValues = new ArrayList<String>();
2341    
2342                    int offset = 0;
2343                    int pos = s.indexOf(delimiter, offset);
2344    
2345                    while (pos != -1) {
2346                            nodeValues.add(s.substring(offset, pos));
2347    
2348                            offset = pos + delimiter.length();
2349                            pos = s.indexOf(delimiter, offset);
2350                    }
2351    
2352                    if (offset < s.length()) {
2353                            nodeValues.add(s.substring(offset));
2354                    }
2355    
2356                    return nodeValues.toArray(new String[nodeValues.size()]);
2357            }
2358    
2359            /**
2360             * Splits the string <code>s</code> around the specified delimiter returning
2361             * the boolean values of the substrings.
2362             *
2363             * @param  s the string to split
2364             * @param  delimiter the delimiter
2365             * @param  x the default value to use for a substring in case an exception
2366             *         occurs in getting the boolean value for that substring
2367             * @return the array of booleans resulting from splitting string
2368             *         <code>s</code> around the specified delimiter string, or an empty
2369             *         array if <code>s</code> is <code>null</code>
2370             */
2371            public static boolean[] split(String s, String delimiter, boolean x) {
2372                    String[] array = split(s, delimiter);
2373                    boolean[] newArray = new boolean[array.length];
2374    
2375                    for (int i = 0; i < array.length; i++) {
2376                            boolean value = x;
2377    
2378                            try {
2379                                    value = Boolean.valueOf(array[i]).booleanValue();
2380                            }
2381                            catch (Exception e) {
2382                            }
2383    
2384                            newArray[i] = value;
2385                    }
2386    
2387                    return newArray;
2388            }
2389    
2390            /**
2391             * Splits the string <code>s</code> around the specified delimiter returning
2392             * the double-precision decimal values of the substrings.
2393             *
2394             * @param  s the string to split
2395             * @param  delimiter the delimiter
2396             * @param  x the default value to use for a substring in case an exception
2397             *         occurs in getting the double-precision decimal value for that
2398             *         substring
2399             * @return the array of double-precision decimal values resulting from
2400             *         splitting string <code>s</code> around the specified delimiter
2401             *         string, or an empty array if <code>s</code> is <code>null</code>
2402             */
2403            public static double[] split(String s, String delimiter, double x) {
2404                    String[] array = split(s, delimiter);
2405                    double[] newArray = new double[array.length];
2406    
2407                    for (int i = 0; i < array.length; i++) {
2408                            double value = x;
2409    
2410                            try {
2411                                    value = Double.parseDouble(array[i]);
2412                            }
2413                            catch (Exception e) {
2414                            }
2415    
2416                            newArray[i] = value;
2417                    }
2418    
2419                    return newArray;
2420            }
2421    
2422            /**
2423             * Splits the string <code>s</code> around the specified delimiter returning
2424             * the decimal values of the substrings.
2425             *
2426             * @param  s the string to split
2427             * @param  delimiter the delimiter
2428             * @param  x the default value to use for a substring in case an exception
2429             *         occurs in getting the decimal value for that substring
2430             * @return the array of decimal values resulting from splitting string
2431             *         <code>s</code> around the specified delimiter string, or an empty
2432             *         array if <code>s</code> is <code>null</code>
2433             */
2434            public static float[] split(String s, String delimiter, float x) {
2435                    String[] array = split(s, delimiter);
2436                    float[] newArray = new float[array.length];
2437    
2438                    for (int i = 0; i < array.length; i++) {
2439                            float value = x;
2440    
2441                            try {
2442                                    value = Float.parseFloat(array[i]);
2443                            }
2444                            catch (Exception e) {
2445                            }
2446    
2447                            newArray[i] = value;
2448                    }
2449    
2450                    return newArray;
2451            }
2452    
2453            /**
2454             * Splits the string <code>s</code> around the specified delimiter returning
2455             * the integer values of the substrings.
2456             *
2457             * @param  s the string to split
2458             * @param  delimiter the delimiter
2459             * @param  x the default value to use for a substring in case an exception
2460             *         occurs in getting the integer value for that substring
2461             * @return the array of integer values resulting from splitting string
2462             *         <code>s</code> around the specified delimiter string, or an empty
2463             *         array if <code>s</code> is <code>null</code>
2464             */
2465            public static int[] split(String s, String delimiter, int x) {
2466                    String[] array = split(s, delimiter);
2467                    int[] newArray = new int[array.length];
2468    
2469                    for (int i = 0; i < array.length; i++) {
2470                            int value = x;
2471    
2472                            try {
2473                                    value = Integer.parseInt(array[i]);
2474                            }
2475                            catch (Exception e) {
2476                            }
2477    
2478                            newArray[i] = value;
2479                    }
2480    
2481                    return newArray;
2482            }
2483    
2484            /**
2485             * Splits the string <code>s</code> around the specified delimiter returning
2486             * the long integer values of the substrings.
2487             *
2488             * @param  s the string to split
2489             * @param  delimiter the delimiter
2490             * @param  x the default value to use for a substring in case an exception
2491             *         occurs in getting the long integer value for that substring
2492             * @return the array of long integer values resulting from splitting string
2493             *         <code>s</code> around the specified delimiter string, or an empty
2494             *         array if <code>s</code> is <code>null</code>
2495             */
2496            public static long[] split(String s, String delimiter, long x) {
2497                    String[] array = split(s, delimiter);
2498                    long[] newArray = new long[array.length];
2499    
2500                    for (int i = 0; i < array.length; i++) {
2501                            long value = x;
2502    
2503                            try {
2504                                    value = Long.parseLong(array[i]);
2505                            }
2506                            catch (Exception e) {
2507                            }
2508    
2509                            newArray[i] = value;
2510                    }
2511    
2512                    return newArray;
2513            }
2514    
2515            /**
2516             * Splits the string <code>s</code> around the specified delimiter returning
2517             * the short integer values of the substrings.
2518             *
2519             * @param  s the string to split
2520             * @param  delimiter the delimiter
2521             * @param  x the default value to use for a substring in case an exception
2522             *         occurs in getting the short integer value for that substring
2523             * @return the array of short integer values resulting from splitting string
2524             *         <code>s</code> around the specified delimiter string, or an empty
2525             *         array if <code>s</code> is <code>null</code>
2526             */
2527            public static short[] split(String s, String delimiter, short x) {
2528                    String[] array = split(s, delimiter);
2529                    short[] newArray = new short[array.length];
2530    
2531                    for (int i = 0; i < array.length; i++) {
2532                            short value = x;
2533    
2534                            try {
2535                                    value = Short.parseShort(array[i]);
2536                            }
2537                            catch (Exception e) {
2538                            }
2539    
2540                            newArray[i] = value;
2541                    }
2542    
2543                    return newArray;
2544            }
2545    
2546            /**
2547             * Splits string <code>s</code> around return and newline characters.
2548             *
2549             * <p>
2550             * Example:
2551             * </p>
2552             *
2553             * <pre>
2554             * <code>
2555             * splitLines("Red\rBlue\nGreen") returns {"Red","Blue","Green"}
2556             * </code>
2557             * </pre>
2558             *
2559             * @param  s the string to split
2560             * @return the array of strings resulting from splitting string
2561             *         <code>s</code> around return and newline characters, or an empty
2562             *         string array if string <code>s</code> is <code>null</code>
2563             */
2564            public static String[] splitLines(String s) {
2565                    if (Validator.isNull(s)) {
2566                            return _emptyStringArray;
2567                    }
2568    
2569                    s = s.trim();
2570    
2571                    List<String> lines = new ArrayList<String>();
2572    
2573                    int lastIndex = 0;
2574    
2575                    while (true) {
2576                            int returnIndex = s.indexOf(CharPool.RETURN, lastIndex);
2577                            int newLineIndex = s.indexOf(CharPool.NEW_LINE, lastIndex);
2578    
2579                            if ((returnIndex == -1) && (newLineIndex == -1)) {
2580                                    break;
2581                            }
2582    
2583                            if (returnIndex == -1) {
2584                                    lines.add(s.substring(lastIndex, newLineIndex));
2585    
2586                                    lastIndex = newLineIndex + 1;
2587                            }
2588                            else if (newLineIndex == -1) {
2589                                    lines.add(s.substring(lastIndex, returnIndex));
2590    
2591                                    lastIndex = returnIndex + 1;
2592                            }
2593                            else if (newLineIndex < returnIndex) {
2594                                    lines.add(s.substring(lastIndex, newLineIndex));
2595    
2596                                    lastIndex = newLineIndex + 1;
2597                            }
2598                            else {
2599                                    lines.add(s.substring(lastIndex, returnIndex));
2600    
2601                                    lastIndex = returnIndex + 1;
2602    
2603                                    if (lastIndex == newLineIndex) {
2604                                            lastIndex++;
2605                                    }
2606                            }
2607                    }
2608    
2609                    if (lastIndex < s.length()) {
2610                            lines.add(s.substring(lastIndex));
2611                    }
2612    
2613                    return lines.toArray(new String[lines.size()]);
2614            }
2615    
2616            /**
2617             * Returns <code>true</code> if, ignoring case, the string starts with the
2618             * specified character.
2619             *
2620             * @param  s the string
2621             * @param  begin the character against which the initial character of the
2622             *         string is to be compared
2623             * @return <code>true</code> if, ignoring case, the string starts with the
2624             *         specified character; <code>false</code> otherwise
2625             */
2626            public static boolean startsWith(String s, char begin) {
2627                    return startsWith(s, (new Character(begin)).toString());
2628            }
2629    
2630            /**
2631             * Returns <code>true</code> if, ignoring case, the string starts with the
2632             * specified start string.
2633             *
2634             * @param  s the original string
2635             * @param  start the string against which the beginning of string
2636             *         <code>s</code> are to be compared
2637             * @return <code>true</code> if, ignoring case, the string starts with the
2638             *         specified start string; <code>false</code> otherwise
2639             */
2640            public static boolean startsWith(String s, String start) {
2641                    if ((s == null) || (start == null)) {
2642                            return false;
2643                    }
2644    
2645                    if (start.length() > s.length()) {
2646                            return false;
2647                    }
2648    
2649                    String temp = s.substring(0, start.length());
2650    
2651                    if (temp.equalsIgnoreCase(start)) {
2652                            return true;
2653                    }
2654                    else {
2655                            return false;
2656                    }
2657            }
2658    
2659            /**
2660             * Returns the number of starting characters that <code>s1</code> and
2661             * <code>s2</code> have in common before their characters deviate.
2662             *
2663             * @param  s1 string 1
2664             * @param  s2 string 2
2665             * @return the number of starting characters that <code>s1</code> and
2666             *         <code>s2</code> have in common before their characters deviate
2667             */
2668            public static int startsWithWeight(String s1, String s2) {
2669                    if ((s1 == null) || (s2 == null)) {
2670                            return 0;
2671                    }
2672    
2673                    char[] chars1 = s1.toCharArray();
2674                    char[] chars2 = s2.toCharArray();
2675    
2676                    int i = 0;
2677    
2678                    for (; (i < chars1.length) && (i < chars2.length); i++) {
2679                            if (chars1[i] != chars2[i]) {
2680                                    break;
2681                            }
2682                    }
2683    
2684                    return i;
2685            }
2686    
2687            /**
2688             * Returns a string representing the string <code>s</code> with all
2689             * occurrences of the specified character removed.
2690             *
2691             * <p>
2692             * Example:
2693             * </p>
2694             *
2695             * <pre>
2696             * <code>
2697             * strip("Mississipi", 'i') returns "Mssssp"
2698             * </code>
2699             * </pre>
2700             *
2701             * @param  s the string from which to strip all occurrences the character
2702             * @param  remove the character to strip from the string
2703             * @return a string representing the string <code>s</code> with all
2704             *         occurrences of the specified character removed, or
2705             *         <code>null</code> if <code>s</code> is <code>null</code>
2706             */
2707            public static String strip(String s, char remove) {
2708                    if (s == null) {
2709                            return null;
2710                    }
2711    
2712                    int x = s.indexOf(remove);
2713    
2714                    if (x < 0) {
2715                            return s;
2716                    }
2717    
2718                    int y = 0;
2719    
2720                    StringBundler sb = new StringBundler(s.length());
2721    
2722                    while (x >= 0) {
2723                            sb.append(s.subSequence(y, x));
2724    
2725                            y = x + 1;
2726    
2727                            x = s.indexOf(remove, y);
2728                    }
2729    
2730                    sb.append(s.substring(y));
2731    
2732                    return sb.toString();
2733            }
2734    
2735            /**
2736             * Returns a string representing the combination of the substring of
2737             * <code>s</code> up to but not including the string <code>begin</code>
2738             * concatenated with the substring of <code>s</code> after but not including
2739             * the string <code>end</code>.
2740             *
2741             * <p>
2742             * Example:
2743             * <p>
2744             *
2745             * <pre>
2746             * <code>
2747             * stripBetween("One small step for man, one giant leap for mankind", "step", "giant ") returns "One small leap for mankind"
2748             * </code>
2749             * </pre>
2750             *
2751             * @param  s the from which to strip a substring
2752             * @param  begin the beginning characters of the substring to be removed
2753             * @param  end the ending characters of the substring to be removed
2754             * @return a string representing the combination of the substring of
2755             *         <code>s</code> up to but not including the string
2756             *         <code>begin</code> concatenated with the substring of
2757             *         <code>s</code> after but not including the string
2758             *         <code>end</code>, or the original string if the value of
2759             *         <code>s</code>, <code>begin</code>, or <code>end</code> are
2760             *         <code>null</code>
2761             */
2762            public static String stripBetween(String s, String begin, String end) {
2763                    if ((s == null) || (begin == null) || (end == null)) {
2764                            return s;
2765                    }
2766    
2767                    StringBundler sb = new StringBundler(s.length());
2768    
2769                    int pos = 0;
2770    
2771                    while (true) {
2772                            int x = s.indexOf(begin, pos);
2773                            int y = s.indexOf(end, x + begin.length());
2774    
2775                            if ((x == -1) || (y == -1)) {
2776                                    sb.append(s.substring(pos));
2777    
2778                                    break;
2779                            }
2780                            else {
2781                                    sb.append(s.substring(pos, x));
2782    
2783                                    pos = y + end.length();
2784                            }
2785                    }
2786    
2787                    return sb.toString();
2788            }
2789    
2790            /**
2791             * Returns a string representing the Unicode character codes of the
2792             * characters comprising the string <code>s</code>.
2793             *
2794             * <p>
2795             * Example:
2796             * </p>
2797             *
2798             * <pre>
2799             * <code>
2800             * toCharCode("a") returns "97"
2801             * toCharCode("b") returns "98"
2802             * toCharCode("c") returns "99"
2803             * toCharCode("What's for lunch?") returns "87104971163911532102111114321081171109910463"
2804             * </code>
2805             * </p>
2806             *
2807             * @param  s the string whose character codes are to be represented
2808             * @return a string representing the Unicode character codes of the
2809             *         characters comprising the string <code>s</code>
2810             */
2811            public static String toCharCode(String s) {
2812                    StringBundler sb = new StringBundler(s.length());
2813    
2814                    for (int i = 0; i < s.length(); i++) {
2815                            sb.append(s.codePointAt(i));
2816                    }
2817    
2818                    return sb.toString();
2819            }
2820    
2821            public static String toHexString(int i) {
2822                    char[] buffer = new char[8];
2823    
2824                    int index = 8;
2825    
2826                    do {
2827                            buffer[--index] = _HEX_DIGITS[i & 15];
2828    
2829                            i >>>= 4;
2830                    }
2831                    while (i != 0);
2832    
2833                    return new String(buffer, index, 8 - index);
2834            }
2835    
2836            public static String toHexString(long l) {
2837                    char[] buffer = new char[16];
2838    
2839                    int index = 16;
2840    
2841                    do {
2842                            buffer[--index] = _HEX_DIGITS[(int) (l & 15)];
2843    
2844                            l >>>= 4;
2845                    }
2846                    while (l != 0);
2847    
2848                    return new String(buffer, index, 16 - index);
2849            }
2850    
2851            public static String toHexString(Object obj) {
2852                    if (obj instanceof Integer) {
2853                            return toHexString(((Integer)obj).intValue());
2854                    }
2855                    else if (obj instanceof Long) {
2856                            return toHexString(((Long)obj).longValue());
2857                    }
2858                    else {
2859                            return String.valueOf(obj);
2860                    }
2861            }
2862    
2863            /**
2864             * Trims all leading and trailing whitespace from the string.
2865             *
2866             * @param  s the original string
2867             * @return a string representing the original string with all leading and
2868             *         trailing whitespace removed
2869             */
2870            public static String trim(String s) {
2871                    if (s == null) {
2872                            return null;
2873                    }
2874    
2875                    if (s.length() == 0) {
2876                            return s;
2877                    }
2878    
2879                    int len = s.length();
2880    
2881                    int x = len;
2882    
2883                    for (int i = 0; i < len; i++) {
2884                            char c = s.charAt(i);
2885    
2886                            if (!Character.isWhitespace(c)) {
2887                                    x = i;
2888    
2889                                    break;
2890                            }
2891                    }
2892    
2893                    if (x == len) {
2894                            return StringPool.BLANK;
2895                    }
2896    
2897                    int y = x + 1;
2898    
2899                    for (int i = len - 1; i > x; i--) {
2900                            char c = s.charAt(i);
2901    
2902                            if (!Character.isWhitespace(c)) {
2903                                    y = i + 1;
2904    
2905                                    break;
2906                            }
2907                    }
2908    
2909                    if ((x == 0) && (y == len)) {
2910                            return s;
2911                    }
2912    
2913                    return s.substring(x, y);
2914            }
2915    
2916            /**
2917             * Trims leading and trailing whitespace from the string, up to but not
2918             * including the whitespace character specified by <code>c</code>.
2919             *
2920             * <p>
2921             * Examples:
2922             * </p>
2923             *
2924             * <pre>
2925             * <code>
2926             * trim(" \tHey\t ", '\t') returns "\tHey\t"
2927             * trim(" \t Hey \t ", '\t') returns "\t Hey \t"
2928             * </code>
2929             * </pre>
2930             *
2931             * @param  s the original string
2932             * @param  c the whitespace character to limit trimming
2933             * @return a string representing the original string with leading and
2934             *         trailing whitespace removed, up to but not including the
2935             *         whitespace character specified by <code>c</code>
2936             */
2937            public static String trim(String s, char c) {
2938                    return trim(s, new char[] {c});
2939            }
2940    
2941            /**
2942             * Trims leading and trailing whitespace from the string, up to but not
2943             * including the whitespace characters specified by <code>exceptions</code>.
2944             *
2945             * @param  s the original string
2946             * @param  exceptions the whitespace characters to limit trimming
2947             * @return a string representing the original string with leading and
2948             *         trailing whitespace removed, up to but not including the
2949             *         whitespace characters specified by <code>exceptions</code>
2950             */
2951            public static String trim(String s, char[] exceptions) {
2952                    if (s == null) {
2953                            return null;
2954                    }
2955    
2956                    if (s.length() == 0) {
2957                            return s;
2958                    }
2959    
2960                    if ((exceptions == null) || (exceptions.length == 0)) {
2961                            return trim(s);
2962                    }
2963    
2964                    int len = s.length();
2965                    int x = len;
2966    
2967                    for (int i = 0; i < len; i++) {
2968                            char c = s.charAt(i);
2969    
2970                            if (!_isTrimable(c, exceptions)) {
2971                                    x = i;
2972    
2973                                    break;
2974                            }
2975                    }
2976    
2977                    if (x == len) {
2978                            return StringPool.BLANK;
2979                    }
2980    
2981                    int y = x + 1;
2982    
2983                    for (int i = len - 1; i > x; i--) {
2984                            char c = s.charAt(i);
2985    
2986                            if (!_isTrimable(c, exceptions)) {
2987                                    y = i + 1;
2988    
2989                                    break;
2990                            }
2991                    }
2992    
2993                    if ((x == 0) && (y == len)) {
2994                            return s;
2995                    }
2996                    else {
2997                            return s.substring(x, y);
2998                    }
2999            }
3000    
3001            /**
3002             * Trims all leading whitespace from the string.
3003             *
3004             * @param  s the original string
3005             * @return a string representing the original string with all leading
3006             *         whitespace removed
3007             */
3008            public static String trimLeading(String s) {
3009                    if (s == null) {
3010                            return null;
3011                    }
3012    
3013                    if (s.length() == 0) {
3014                            return s;
3015                    }
3016    
3017                    int len = s.length();
3018                    int x = len;
3019    
3020                    for (int i = 0; i < len; i++) {
3021                            char c = s.charAt(i);
3022    
3023                            if (!Character.isWhitespace(c)) {
3024                                    x = i;
3025    
3026                                    break;
3027                            }
3028                    }
3029    
3030                    if (x == len) {
3031                            return StringPool.BLANK;
3032                    }
3033                    else if (x == 0) {
3034                            return s;
3035                    }
3036                    else {
3037                            return s.substring(x);
3038                    }
3039            }
3040    
3041            /**
3042             * Trims leading whitespace from the string, up to but not including the
3043             * whitespace character specified by <code>c</code>.
3044             *
3045             * @param  s the original string
3046             * @param  c the whitespace character to limit trimming
3047             * @return a string representing the original string with leading whitespace
3048             *         removed, up to but not including the whitespace character
3049             *         specified by <code>c</code>
3050             */
3051            public static String trimLeading(String s, char c) {
3052                    return trimLeading(s, new char[] {c});
3053            }
3054    
3055            /**
3056             * Trims leading whitespace from the string, up to but not including the
3057             * whitespace characters specified by <code>exceptions</code>.
3058             *
3059             * @param  s the original string
3060             * @param  exceptions the whitespace characters to limit trimming
3061             * @return a string representing the original string with leading whitespace
3062             *         removed, up to but not including the whitespace characters
3063             *         specified by <code>exceptions</code>
3064             */
3065            public static String trimLeading(String s, char[] exceptions) {
3066                    if (s == null) {
3067                            return null;
3068                    }
3069    
3070                    if (s.length() == 0) {
3071                            return s;
3072                    }
3073    
3074                    if ((exceptions == null) || (exceptions.length == 0)) {
3075                            return trimLeading(s);
3076                    }
3077    
3078                    int len = s.length();
3079                    int x = len;
3080    
3081                    for (int i = 0; i < len; i++) {
3082                            char c = s.charAt(i);
3083    
3084                            if (!_isTrimable(c, exceptions)) {
3085                                    x = i;
3086    
3087                                    break;
3088                            }
3089                    }
3090    
3091                    if (x == len) {
3092                            return StringPool.BLANK;
3093                    }
3094                    else if (x == 0) {
3095                            return s;
3096                    }
3097                    else {
3098                            return s.substring(x);
3099                    }
3100            }
3101    
3102            /**
3103             * Trims all trailing whitespace from the string.
3104             *
3105             * @param  s the original string
3106             * @return a string representing the original string with all trailing
3107             *         whitespace removed
3108             */
3109            public static String trimTrailing(String s) {
3110                    if (s == null) {
3111                            return null;
3112                    }
3113    
3114                    if (s.length() == 0) {
3115                            return s;
3116                    }
3117    
3118                    int len = s.length();
3119                    int x = 0;
3120    
3121                    for (int i = len - 1; i >= 0; i--) {
3122                            char c = s.charAt(i);
3123    
3124                            if (!Character.isWhitespace(c)) {
3125                                    x = i + 1;
3126    
3127                                    break;
3128                            }
3129                    }
3130    
3131                    if (x == 0) {
3132                            return StringPool.BLANK;
3133                    }
3134                    else if (x == len) {
3135                            return s;
3136                    }
3137                    else {
3138                            return s.substring(0, x);
3139                    }
3140            }
3141    
3142            /**
3143             * Trims trailing whitespace from the string, up to but not including the
3144             * whitespace character specified by <code>c</code>.
3145             *
3146             * @param  s the original string
3147             * @param  c the whitespace character to limit trimming
3148             * @return a string representing the original string with trailing
3149             *         whitespace removed, up to but not including the whitespace
3150             *         character specified by <code>c</code>
3151             */
3152            public static String trimTrailing(String s, char c) {
3153                    return trimTrailing(s, new char[] {c});
3154            }
3155    
3156            /**
3157             * Trims trailing whitespace from the string, up to but not including the
3158             * whitespace characters specified by <code>exceptions</code>.
3159             *
3160             * @param  s the original string
3161             * @param  exceptions the whitespace characters to limit trimming
3162             * @return a string representing the original string with trailing
3163             *         whitespace removed, up to but not including the whitespace
3164             *         characters specified by <code>exceptions</code>
3165             */
3166            public static String trimTrailing(String s, char[] exceptions) {
3167                    if (s == null) {
3168                            return null;
3169                    }
3170    
3171                    if (s.length() == 0) {
3172                            return s;
3173                    }
3174    
3175                    if ((exceptions == null) || (exceptions.length == 0)) {
3176                            return trimTrailing(s);
3177                    }
3178    
3179                    int len = s.length();
3180                    int x = 0;
3181    
3182                    for (int i = len - 1; i >= 0; i--) {
3183                            char c = s.charAt(i);
3184    
3185                            if (!_isTrimable(c, exceptions)) {
3186                                    x = i + 1;
3187    
3188                                    break;
3189                            }
3190                    }
3191    
3192                    if (x == 0) {
3193                            return StringPool.BLANK;
3194                    }
3195                    else if (x == len) {
3196                            return s;
3197                    }
3198                    else {
3199                            return s.substring(0, x);
3200                    }
3201            }
3202    
3203            /**
3204             * Removes leading and trailing double and single quotation marks from the
3205             * string.
3206             *
3207             * @param  s the original string
3208             * @return a string representing the original string with leading and
3209             *         trailing double and single quotation marks removed, or the
3210             *         original string if the original string is a <code>null</code> or
3211             *         empty
3212             */
3213            public static String unquote(String s) {
3214                    if (Validator.isNull(s)) {
3215                            return s;
3216                    }
3217    
3218                    if ((s.charAt(0) == CharPool.APOSTROPHE) &&
3219                            (s.charAt(s.length() - 1) == CharPool.APOSTROPHE)) {
3220    
3221                            return s.substring(1, s.length() - 1);
3222                    }
3223                    else if ((s.charAt(0) == CharPool.QUOTE) &&
3224                                     (s.charAt(s.length() - 1) == CharPool.QUOTE)) {
3225    
3226                            return s.substring(1, s.length() - 1);
3227                    }
3228    
3229                    return s;
3230            }
3231    
3232            /**
3233             * Converts all of the characters in the string to upper case.
3234             *
3235             * @param  s the string to convert
3236             * @return the string, converted to upper-case, or <code>null</code> if the
3237             *         string is <code>null</code>
3238             * @see    String#toUpperCase()
3239             */
3240            public static String upperCase(String s) {
3241                    if (s == null) {
3242                            return null;
3243                    }
3244                    else {
3245                            return s.toUpperCase();
3246                    }
3247            }
3248    
3249            /**
3250             * Converts the first character of the string to upper case.
3251             *
3252             * @param  s the string whose first character is to be converted
3253             * @return the string, with its first character converted to upper-case
3254             */
3255            public static String upperCaseFirstLetter(String s) {
3256                    char[] chars = s.toCharArray();
3257    
3258                    if ((chars[0] >= 97) && (chars[0] <= 122)) {
3259                            chars[0] = (char)(chars[0] - 32);
3260                    }
3261    
3262                    return new String(chars);
3263            }
3264    
3265            /**
3266             * Returns the string value of the object.
3267             *
3268             * @param  obj the object whose string value is to be returned
3269             * @return the string value of the object
3270             * @see    String#valueOf(Object obj)
3271             */
3272            public static String valueOf(Object obj) {
3273                    return String.valueOf(obj);
3274            }
3275    
3276            public static String wrap(String text) {
3277                    return wrap(text, 80, StringPool.NEW_LINE);
3278            }
3279    
3280            public static String wrap(String text, int width, String lineSeparator) {
3281                    try {
3282                            return _wrap(text, width, lineSeparator);
3283                    }
3284                    catch (IOException ioe) {
3285                            _log.error(ioe.getMessage());
3286    
3287                            return text;
3288                    }
3289            }
3290    
3291            private static String _highlight(
3292                    String s, Pattern pattern, String highlight1, String highlight2) {
3293    
3294                    StringTokenizer st = new StringTokenizer(s);
3295    
3296                    if (st.countTokens() == 0) {
3297                            return StringPool.BLANK;
3298                    }
3299    
3300                    StringBundler sb = new StringBundler(2 * st.countTokens() - 1);
3301    
3302                    while (st.hasMoreTokens()) {
3303                            String token = st.nextToken();
3304    
3305                            Matcher matcher = pattern.matcher(token);
3306    
3307                            if (matcher.find()) {
3308                                    StringBuffer hightlighted = new StringBuffer();
3309    
3310                                    do {
3311                                            matcher.appendReplacement(
3312                                                    hightlighted, highlight1 + matcher.group() +
3313                                                    highlight2);
3314                                    }
3315                                    while (matcher.find());
3316    
3317                                    matcher.appendTail(hightlighted);
3318    
3319                                    sb.append(hightlighted);
3320                            }
3321                            else {
3322                                    sb.append(token);
3323                            }
3324    
3325                            if (st.hasMoreTokens()) {
3326                                    sb.append(StringPool.SPACE);
3327                            }
3328                    }
3329    
3330                    return sb.toString();
3331            }
3332    
3333            /**
3334             * Returns <code>false</code> if the character is not whitespace or is equal
3335             * to any of the exception characters.
3336             *
3337             * @param  c the character whose trim-ability is to be determined
3338             * @param  exceptions the whitespace characters to exclude from trimming
3339             * @return <code>false</code> if the character is not whitespace or is equal
3340             *         to any of the exception characters; <code>true</code> otherwise
3341             */
3342            private static boolean _isTrimable(char c, char[] exceptions) {
3343                    for (char exception : exceptions) {
3344                            if (c == exception) {
3345                                    return false;
3346                            }
3347                    }
3348    
3349                    return Character.isWhitespace(c);
3350            }
3351    
3352            private static String _wrap(String text, int width, String lineSeparator)
3353                    throws IOException {
3354    
3355                    if (text == null) {
3356                            return null;
3357                    }
3358    
3359                    StringBundler sb = new StringBundler();
3360    
3361                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
3362                            new UnsyncStringReader(text));
3363    
3364                    String s = StringPool.BLANK;
3365    
3366                    while ((s = unsyncBufferedReader.readLine()) != null) {
3367                            if (s.length() == 0) {
3368                                    sb.append(lineSeparator);
3369    
3370                                    continue;
3371                            }
3372    
3373                            int lineLength = 0;
3374    
3375                            String[] tokens = s.split(StringPool.SPACE);
3376    
3377                            for (String token : tokens) {
3378                                    if ((lineLength + token.length() + 1) > width) {
3379                                            if (lineLength > 0) {
3380                                                    sb.append(lineSeparator);
3381                                            }
3382    
3383                                            if (token.length() > width) {
3384                                                    int pos = token.indexOf(CharPool.OPEN_PARENTHESIS);
3385    
3386                                                    if (pos != -1) {
3387                                                            sb.append(token.substring(0, pos + 1));
3388                                                            sb.append(lineSeparator);
3389    
3390                                                            token = token.substring(pos + 1);
3391    
3392                                                            sb.append(token);
3393    
3394                                                            lineLength = token.length();
3395                                                    }
3396                                                    else {
3397                                                            sb.append(token);
3398    
3399                                                            lineLength = token.length();
3400                                                    }
3401                                            }
3402                                            else {
3403                                                    sb.append(token);
3404    
3405                                                    lineLength = token.length();
3406                                            }
3407                                    }
3408                                    else {
3409                                            if (lineLength > 0) {
3410                                                    sb.append(StringPool.SPACE);
3411    
3412                                                    lineLength++;
3413                                            }
3414    
3415                                            sb.append(token);
3416    
3417                                            lineLength += token.length();
3418                                    }
3419                            }
3420    
3421                            sb.append(lineSeparator);
3422                    }
3423    
3424                    return sb.toString();
3425            }
3426    
3427            private static final char[] _HEX_DIGITS = {
3428                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
3429                    'e', 'f'
3430            };
3431    
3432            private static Log _log = LogFactoryUtil.getLog(StringUtil.class);
3433    
3434            private static String[] _emptyStringArray = new String[0];
3435    
3436    }