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