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