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