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