1   /**
2    * Copyright (c) 2000-2007 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.util;
24  
25  import com.liferay.portal.kernel.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  
28  import java.io.BufferedReader;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.StringReader;
33  
34  import java.net.URL;
35  
36  import java.util.ArrayList;
37  import java.util.Enumeration;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.StringTokenizer;
41  
42  /**
43   * <a href="StringUtil.java.html"><b><i>View Source</i></b></a>
44   *
45   * @author Brian Wing Shun Chan
46   *
47   */
48  public class StringUtil {
49  
50      public static String add(String s, String add) {
51          return add(s, add, StringPool.COMMA);
52      }
53  
54      public static String add(String s, String add, String delimiter) {
55          return add(s, add, delimiter, false);
56      }
57  
58      public static String add(
59          String s, String add, String delimiter, boolean allowDuplicates) {
60  
61          if ((add == null) || (delimiter == null)) {
62              return null;
63          }
64  
65          if (s == null) {
66              s = StringPool.BLANK;
67          }
68  
69          if (allowDuplicates || !contains(s, add, delimiter)) {
70              StringMaker sm = new StringMaker();
71  
72              sm.append(s);
73  
74              if (Validator.isNull(s) || s.endsWith(delimiter)) {
75                  sm.append(add);
76                  sm.append(delimiter);
77              }
78              else {
79                  sm.append(delimiter);
80                  sm.append(add);
81                  sm.append(delimiter);
82              }
83  
84              s = sm.toString();
85          }
86  
87          return s;
88      }
89  
90      public static String bytesToHexString(byte[] bytes) {
91          StringMaker sm = new StringMaker(bytes.length * 2);
92  
93          for (int i = 0; i < bytes.length; i++) {
94              String hex = Integer.toHexString(
95                  0x0100 + (bytes[i] & 0x00FF)).substring(1);
96  
97              if (hex.length() < 2) {
98                  sm.append("0");
99              }
100 
101             sm.append(hex);
102         }
103 
104         return sm.toString();
105     }
106 
107     public static boolean contains(String s, String text) {
108         return contains(s, text, StringPool.COMMA);
109     }
110 
111     public static boolean contains(String s, String text, String delimiter) {
112         if ((s == null) || (text == null) || (delimiter == null)) {
113             return false;
114         }
115 
116         StringMaker sm = null;
117 
118         if (!s.endsWith(delimiter)) {
119             sm = new StringMaker();
120 
121             sm.append(s);
122             sm.append(delimiter);
123 
124             s = sm.toString();
125         }
126 
127         sm = new StringMaker();
128 
129         sm.append(delimiter);
130         sm.append(text);
131         sm.append(delimiter);
132 
133         String dtd = sm.toString();
134 
135         int pos = s.indexOf(dtd);
136 
137         if (pos == -1) {
138             sm = new StringMaker();
139 
140             sm.append(text);
141             sm.append(delimiter);
142 
143             String td = sm.toString();
144 
145             if (s.startsWith(td)) {
146                 return true;
147             }
148 
149             return false;
150         }
151 
152         return true;
153     }
154 
155     public static int count(String s, String text) {
156         if ((s == null) || (text == null)) {
157             return 0;
158         }
159 
160         int count = 0;
161 
162         int pos = s.indexOf(text);
163 
164         while (pos != -1) {
165             pos = s.indexOf(text, pos + text.length());
166 
167             count++;
168         }
169 
170         return count;
171     }
172 
173     public static boolean endsWith(String s, char end) {
174         return endsWith(s, (new Character(end)).toString());
175     }
176 
177     public static boolean endsWith(String s, String end) {
178         if ((s == null) || (end == null)) {
179             return false;
180         }
181 
182         if (end.length() > s.length()) {
183             return false;
184         }
185 
186         String temp = s.substring(s.length() - end.length(), s.length());
187 
188         if (temp.equalsIgnoreCase(end)) {
189             return true;
190         }
191         else {
192             return false;
193         }
194     }
195 
196     public static String extractChars(String s) {
197         if (s == null) {
198             return StringPool.BLANK;
199         }
200 
201         StringMaker sm = new StringMaker();
202 
203         char[] c = s.toCharArray();
204 
205         for (int i = 0; i < c.length; i++) {
206             if (Validator.isChar(c[i])) {
207                 sm.append(c[i]);
208             }
209         }
210 
211         return sm.toString();
212     }
213 
214     public static String extractDigits(String s) {
215         if (s == null) {
216             return StringPool.BLANK;
217         }
218 
219         StringMaker sm = new StringMaker();
220 
221         char[] c = s.toCharArray();
222 
223         for (int i = 0; i < c.length; i++) {
224             if (Validator.isDigit(c[i])) {
225                 sm.append(c[i]);
226             }
227         }
228 
229         return sm.toString();
230     }
231 
232     public static String extractFirst(String s, String delimiter) {
233         if (s == null) {
234             return null;
235         }
236         else {
237             String[] array = split(s, delimiter);
238 
239             if (array.length > 0) {
240                 return array[0];
241             }
242             else {
243                 return null;
244             }
245         }
246     }
247 
248     public static String extractLast(String s, String delimiter) {
249         if (s == null) {
250             return null;
251         }
252         else {
253             String[] array = split(s, delimiter);
254 
255             if (array.length > 0) {
256                 return array[array.length - 1];
257             }
258             else {
259                 return null;
260             }
261         }
262     }
263 
264     public static String highlight(String s, String keywords) {
265         return highlight(s, keywords, "<span class=\"highlight\">", "</span>");
266     }
267 
268     public static String highlight(
269         String s, String keywords, String highlight1, String highlight2) {
270 
271         if (s == null) {
272             return null;
273         }
274 
275         // The problem with using a regexp is that it searches the text in a
276         // case insenstive manner but doens't replace the text in a case
277         // insenstive manner. So the search results actually get messed up. The
278         // best way is to actually parse the results.
279 
280         //return s.replaceAll(
281         //  "(?i)" + keywords, highlight1 + keywords + highlight2);
282 
283         StringMaker sm = new StringMaker(StringPool.SPACE);
284 
285         StringTokenizer st = new StringTokenizer(s);
286 
287         while (st.hasMoreTokens()) {
288             String token = st.nextToken();
289 
290             if (token.equalsIgnoreCase(keywords)) {
291                 sm.append(highlight1);
292                 sm.append(token);
293                 sm.append(highlight2);
294             }
295             else {
296                 sm.append(token);
297             }
298 
299             if (st.hasMoreTokens()) {
300                 sm.append(StringPool.SPACE);
301             }
302         }
303 
304         return sm.toString();
305     }
306 
307     public static boolean isEmpty(final String s) {
308         if ((s == null) || (s.length() == 0) || s.equals(StringPool.BLANK)) {
309             return true;
310         }
311         else {
312             return false;
313         }
314     }
315 
316     public static String lowerCase(String s) {
317         if (s == null) {
318             return null;
319         }
320         else {
321             return s.toLowerCase();
322         }
323     }
324 
325     public static String merge(List list) {
326         return merge(list, StringPool.COMMA);
327     }
328 
329     public static String merge(List list, String delimiter) {
330         return merge((Object[])list.toArray(
331             new Object[list.size()]), delimiter);
332     }
333 
334     public static String merge(Object[] array) {
335         return merge(array, StringPool.COMMA);
336     }
337 
338     public static String merge(Object[] array, String delimiter) {
339         if (array == null) {
340             return null;
341         }
342 
343         StringMaker sm = new StringMaker();
344 
345         for (int i = 0; i < array.length; i++) {
346             sm.append(String.valueOf(array[i]).trim());
347 
348             if ((i + 1) != array.length) {
349                 sm.append(delimiter);
350             }
351         }
352 
353         return sm.toString();
354     }
355 
356     public static String randomize(String s) {
357         return Randomizer.getInstance().randomize(s);
358     }
359 
360     public static String read(ClassLoader classLoader, String name)
361         throws IOException {
362 
363         return read(classLoader, name, false);
364     }
365 
366     public static String read(ClassLoader classLoader, String name, boolean all)
367         throws IOException {
368 
369         if (all) {
370             StringMaker sm = new StringMaker();
371 
372             Enumeration enu = classLoader.getResources(name);
373 
374             while (enu.hasMoreElements()) {
375                 URL url = (URL)enu.nextElement();
376 
377                 InputStream is = url.openStream();
378 
379                 String s = read(is);
380 
381                 if (s != null) {
382                     sm.append(s);
383                     sm.append(StringPool.NEW_LINE);
384                 }
385 
386                 is.close();
387             }
388 
389             return sm.toString().trim();
390         }
391         else {
392             InputStream is = classLoader.getResourceAsStream(name);
393 
394             String s = read(is);
395 
396             is.close();
397 
398             return s;
399         }
400     }
401 
402     public static String read(InputStream is) throws IOException {
403         StringMaker sm = new StringMaker();
404 
405         BufferedReader br = new BufferedReader(new InputStreamReader(is));
406 
407         String line = null;
408 
409         while ((line = br.readLine()) != null) {
410             sm.append(line).append('\n');
411         }
412 
413         br.close();
414 
415         return sm.toString().trim();
416     }
417 
418     public static String remove(String s, String remove) {
419         return remove(s, remove, StringPool.COMMA);
420     }
421 
422     public static String remove(String s, String remove, String delimiter) {
423         if ((s == null) || (remove == null) || (delimiter == null)) {
424             return null;
425         }
426 
427         if (Validator.isNotNull(s) && !s.endsWith(delimiter)) {
428             s += delimiter;
429         }
430 
431         StringMaker sm = new StringMaker();
432 
433         sm.append(delimiter);
434         sm.append(remove);
435         sm.append(delimiter);
436 
437         String drd = sm.toString();
438 
439         sm = new StringMaker();
440 
441         sm.append(remove);
442         sm.append(delimiter);
443 
444         String rd = sm.toString();
445 
446         while (contains(s, remove, delimiter)) {
447             int pos = s.indexOf(drd);
448 
449             if (pos == -1) {
450                 if (s.startsWith(rd)) {
451                     int x = remove.length() + delimiter.length();
452                     int y = s.length();
453 
454                     s = s.substring(x, y);
455                 }
456             }
457             else {
458                 int x = pos + remove.length() + delimiter.length();
459                 int y = s.length();
460 
461                 sm = new StringMaker();
462 
463                 sm.append(s.substring(0, pos));
464                 sm.append(s.substring(x, y));
465 
466                 s =  sm.toString();
467             }
468         }
469 
470         return s;
471     }
472 
473     public static String replace(String s, char oldSub, char newSub) {
474         return replace(s, oldSub, new Character(newSub).toString());
475     }
476 
477     public static String replace(String s, char oldSub, String newSub) {
478         if ((s == null) || (newSub == null)) {
479             return null;
480         }
481 
482         StringMaker sm = new StringMaker();
483 
484         char[] c = s.toCharArray();
485 
486         for (int i = 0; i < c.length; i++) {
487             if (c[i] == oldSub) {
488                 sm.append(newSub);
489             }
490             else {
491                 sm.append(c[i]);
492             }
493         }
494 
495         return sm.toString();
496     }
497 
498     public static String replace(String s, String oldSub, String newSub) {
499         if ((s == null) || (oldSub == null) || (newSub == null)) {
500             return null;
501         }
502 
503         int y = s.indexOf(oldSub);
504 
505         if (y >= 0) {
506             StringMaker sm = new StringMaker();
507 
508             int length = oldSub.length();
509             int x = 0;
510 
511             while (x <= y) {
512                 sm.append(s.substring(x, y));
513                 sm.append(newSub);
514                 x = y + length;
515                 y = s.indexOf(oldSub, x);
516             }
517 
518             sm.append(s.substring(x));
519 
520             return sm.toString();
521         }
522         else {
523             return s;
524         }
525     }
526 
527     public static String replace(String s, String[] oldSubs, String[] newSubs) {
528         if ((s == null) || (oldSubs == null) || (newSubs == null)) {
529             return null;
530         }
531 
532         if (oldSubs.length != newSubs.length) {
533             return s;
534         }
535 
536         for (int i = 0; i < oldSubs.length; i++) {
537             s = replace(s, oldSubs[i], newSubs[i]);
538         }
539 
540         return s;
541     }
542 
543     /**
544      * Returns a string with replaced values. This method will replace all text
545      * in the given string, between the beginning and ending delimiter, with new
546      * values found in the given map. For example, if the string contained the
547      * text <code>[$HELLO$]</code>, and the beginning delimiter was
548      * <code>[$]</code>, and the ending delimiter was <code>$]</code>, and the
549      * values map had a key of <code>HELLO</code> that mapped to
550      * <code>WORLD</code>, then the replaced string will contain the text
551      * <code>[$WORLD$]</code>.
552      *
553      * @param       s the original string
554      * @param       begin the beginning delimiter
555      * @param       end the ending delimiter
556      * @param       values a map of old and new values
557      * @return      a string with replaced values
558      */
559     public static String replaceValues(
560         String s, String begin, String end, Map values) {
561 
562         if ((s == null) || (begin == null) || (end == null) ||
563             (values == null) || (values.size() == 0)) {
564 
565             return s;
566         }
567 
568         StringMaker sm = new StringMaker(s.length());
569 
570         int pos = 0;
571 
572         while (true) {
573             int x = s.indexOf(begin, pos);
574             int y = s.indexOf(end, x + begin.length());
575 
576             if ((x == -1) || (y == -1)) {
577                 sm.append(s.substring(pos, s.length()));
578 
579                 break;
580             }
581             else {
582                 sm.append(s.substring(pos, x + begin.length()));
583 
584                 String oldValue = s.substring(x + begin.length(), y);
585 
586                 String newValue = (String)values.get(oldValue);
587 
588                 if (newValue == null) {
589                     newValue = oldValue;
590                 }
591 
592                 sm.append(newValue);
593 
594                 pos = y;
595             }
596         }
597 
598         return sm.toString();
599     }
600 
601     public static String reverse(String s) {
602         if (s == null) {
603             return null;
604         }
605 
606         char[] c = s.toCharArray();
607         char[] reverse = new char[c.length];
608 
609         for (int i = 0; i < c.length; i++) {
610             reverse[i] = c[c.length - i - 1];
611         }
612 
613         return new String(reverse);
614     }
615 
616     public static String safePath(String path) {
617         return StringUtil.replace(
618             path, StringPool.DOUBLE_SLASH, StringPool.SLASH);
619     }
620 
621     public static String shorten(String s) {
622         return shorten(s, 20);
623     }
624 
625     public static String shorten(String s, int length) {
626         return shorten(s, length, "...");
627     }
628 
629     public static String shorten(String s, String suffix) {
630         return shorten(s, 20, suffix);
631     }
632 
633     public static String shorten(String s, int length, String suffix) {
634         if (s == null || suffix == null)  {
635             return null;
636         }
637 
638         if (s.length() > length) {
639             for (int j = length; j >= 0; j--) {
640                 if (Character.isWhitespace(s.charAt(j))) {
641                     length = j;
642 
643                     break;
644                 }
645             }
646 
647             StringMaker sm = new StringMaker();
648 
649             sm.append(s.substring(0, length));
650             sm.append(suffix);
651 
652             s =  sm.toString();
653         }
654 
655         return s;
656     }
657 
658     public static String[] split(String s) {
659         return split(s, StringPool.COMMA);
660     }
661 
662     public static String[] split(String s, String delimiter) {
663         if (s == null || delimiter == null) {
664             return new String[0];
665         }
666 
667         s = s.trim();
668 
669         if (!s.endsWith(delimiter)) {
670             StringMaker sm = new StringMaker();
671 
672             sm.append(s);
673             sm.append(delimiter);
674 
675             s = sm.toString();
676         }
677 
678         if (s.equals(delimiter)) {
679             return new String[0];
680         }
681 
682         List nodeValues = new ArrayList();
683 
684         if (delimiter.equals("\n") || delimiter.equals("\r")) {
685             try {
686                 BufferedReader br = new BufferedReader(new StringReader(s));
687 
688                 String line = null;
689 
690                 while ((line = br.readLine()) != null) {
691                     nodeValues.add(line);
692                 }
693 
694                 br.close();
695             }
696             catch (IOException ioe) {
697                 _log.error(ioe.getMessage());
698             }
699         }
700         else {
701             int offset = 0;
702             int pos = s.indexOf(delimiter, offset);
703 
704             while (pos != -1) {
705                 nodeValues.add(new String(s.substring(offset, pos)));
706 
707                 offset = pos + delimiter.length();
708                 pos = s.indexOf(delimiter, offset);
709             }
710         }
711 
712         return (String[])nodeValues.toArray(new String[nodeValues.size()]);
713     }
714 
715     public static boolean[] split(String s, boolean x) {
716         return split(s, StringPool.COMMA, x);
717     }
718 
719     public static boolean[] split(String s, String delimiter, boolean x) {
720         String[] array = split(s, delimiter);
721         boolean[] newArray = new boolean[array.length];
722 
723         for (int i = 0; i < array.length; i++) {
724             boolean value = x;
725 
726             try {
727                 value = Boolean.valueOf(array[i]).booleanValue();
728             }
729             catch (Exception e) {
730             }
731 
732             newArray[i] = value;
733         }
734 
735         return newArray;
736     }
737 
738     public static double[] split(String s, double x) {
739         return split(s, StringPool.COMMA, x);
740     }
741 
742     public static double[] split(String s, String delimiter, double x) {
743         String[] array = split(s, delimiter);
744         double[] newArray = new double[array.length];
745 
746         for (int i = 0; i < array.length; i++) {
747             double value = x;
748 
749             try {
750                 value = Double.parseDouble(array[i]);
751             }
752             catch (Exception e) {
753             }
754 
755             newArray[i] = value;
756         }
757 
758         return newArray;
759     }
760 
761     public static float[] split(String s, float x) {
762         return split(s, StringPool.COMMA, x);
763     }
764 
765     public static float[] split(String s, String delimiter, float x) {
766         String[] array = split(s, delimiter);
767         float[] newArray = new float[array.length];
768 
769         for (int i = 0; i < array.length; i++) {
770             float value = x;
771 
772             try {
773                 value = Float.parseFloat(array[i]);
774             }
775             catch (Exception e) {
776             }
777 
778             newArray[i] = value;
779         }
780 
781         return newArray;
782     }
783 
784     public static int[] split(String s, int x) {
785         return split(s, StringPool.COMMA, x);
786     }
787 
788     public static int[] split(String s, String delimiter, int x) {
789         String[] array = split(s, delimiter);
790         int[] newArray = new int[array.length];
791 
792         for (int i = 0; i < array.length; i++) {
793             int value = x;
794 
795             try {
796                 value = Integer.parseInt(array[i]);
797             }
798             catch (Exception e) {
799             }
800 
801             newArray[i] = value;
802         }
803 
804         return newArray;
805     }
806 
807     public static long[] split(String s, long x) {
808         return split(s, StringPool.COMMA, x);
809     }
810 
811     public static long[] split(String s, String delimiter, long x) {
812         String[] array = split(s, delimiter);
813         long[] newArray = new long[array.length];
814 
815         for (int i = 0; i < array.length; i++) {
816             long value = x;
817 
818             try {
819                 value = Long.parseLong(array[i]);
820             }
821             catch (Exception e) {
822             }
823 
824             newArray[i] = value;
825         }
826 
827         return newArray;
828     }
829 
830     public static short[] split(String s, short x) {
831         return split(s, StringPool.COMMA, x);
832     }
833 
834     public static short[] split(String s, String delimiter, short x) {
835         String[] array = split(s, delimiter);
836         short[] newArray = new short[array.length];
837 
838         for (int i = 0; i < array.length; i++) {
839             short value = x;
840 
841             try {
842                 value = Short.parseShort(array[i]);
843             }
844             catch (Exception e) {
845             }
846 
847             newArray[i] = value;
848         }
849 
850         return newArray;
851     }
852 
853     public static boolean startsWith(String s, char begin) {
854         return startsWith(s, (new Character(begin)).toString());
855     }
856 
857     public static boolean startsWith(String s, String start) {
858         if ((s == null) || (start == null)) {
859             return false;
860         }
861 
862         if (start.length() > s.length()) {
863             return false;
864         }
865 
866         String temp = s.substring(0, start.length());
867 
868         if (temp.equalsIgnoreCase(start)) {
869             return true;
870         }
871         else {
872             return false;
873         }
874     }
875 
876     public static String stripBetween(String s, String begin, String end) {
877         if ((s == null) || (begin == null) || (end == null)) {
878             return s;
879         }
880 
881         StringMaker sm = new StringMaker(s.length());
882 
883         int pos = 0;
884 
885         while (true) {
886             int x = s.indexOf(begin, pos);
887             int y = s.indexOf(end, x + begin.length());
888 
889             if ((x == -1) || (y == -1)) {
890                 sm.append(s.substring(pos, s.length()));
891 
892                 break;
893             }
894             else {
895                 sm.append(s.substring(pos, x));
896 
897                 pos = y + end.length();
898             }
899         }
900 
901         return sm.toString();
902     }
903 
904     public static String trim(String s) {
905         return trim(s, null);
906     }
907 
908     public static String trim(String s, char c) {
909         return trim(s, new char[] {c});
910     }
911 
912     public static String trim(String s, char[] exceptions) {
913         if (s == null) {
914             return null;
915         }
916 
917         char[] charArray = s.toCharArray();
918 
919         int len = charArray.length;
920 
921         int x = 0;
922         int y = charArray.length;
923 
924         for (int i = 0; i < len; i++) {
925             char c = charArray[i];
926 
927             if (_isTrimable(c, exceptions)) {
928                 x = i + 1;
929             }
930             else {
931                 break;
932             }
933         }
934 
935         for (int i = len - 1; i >= 0; i--) {
936             char c = charArray[i];
937 
938             if (_isTrimable(c, exceptions)) {
939                 y = i;
940             }
941             else {
942                 break;
943             }
944         }
945 
946         if ((x != 0) || (y != len)) {
947             return s.substring(x, y);
948         }
949         else {
950             return s;
951         }
952     }
953 
954     public static String trimLeading(String s) {
955         return trimLeading(s, null);
956     }
957 
958     public static String trimLeading(String s, char c) {
959         return trimLeading(s, new char[] {c});
960     }
961 
962     public static String trimLeading(String s, char[] exceptions) {
963         if (s == null) {
964             return null;
965         }
966 
967         char[] charArray = s.toCharArray();
968 
969         int len = charArray.length;
970 
971         int x = 0;
972         int y = charArray.length;
973 
974         for (int i = 0; i < len; i++) {
975             char c = charArray[i];
976 
977             if (_isTrimable(c, exceptions)) {
978                 x = i + 1;
979             }
980             else {
981                 break;
982             }
983         }
984 
985         if ((x != 0) || (y != len)) {
986             return s.substring(x, y);
987         }
988         else {
989             return s;
990         }
991     }
992 
993     public static String trimTrailing(String s) {
994         return trimTrailing(s, null);
995     }
996 
997     public static String trimTrailing(String s, char c) {
998         return trimTrailing(s, new char[] {c});
999     }
1000
1001    public static String trimTrailing(String s, char[] exceptions) {
1002        if (s == null) {
1003            return null;
1004        }
1005
1006        char[] charArray = s.toCharArray();
1007
1008        int len = charArray.length;
1009
1010        int x = 0;
1011        int y = charArray.length;
1012
1013        for (int i = len - 1; i >= 0; i--) {
1014            char c = charArray[i];
1015
1016            if (_isTrimable(c, exceptions)) {
1017                y = i;
1018            }
1019            else {
1020                break;
1021            }
1022        }
1023
1024        if ((x != 0) || (y != len)) {
1025            return s.substring(x, y);
1026        }
1027        else {
1028            return s;
1029        }
1030    }
1031
1032    public static String upperCase(String s) {
1033        if (s == null) {
1034            return null;
1035        }
1036        else {
1037            return s.toUpperCase();
1038        }
1039    }
1040
1041    public static String upperCaseFirstLetter(String s) {
1042        char[] chars = s.toCharArray();
1043
1044        if (chars[0] >= 97 && chars[0] <= 122) {
1045            chars[0] = (char) ((int) chars[0] - 32);
1046        }
1047
1048        return new String(chars);
1049    }
1050
1051    public static String wrap(String text) {
1052        return wrap(text, 80, "\n");
1053    }
1054
1055    public static String wrap(String text, int width, String lineSeparator) {
1056        if (text == null) {
1057            return null;
1058        }
1059
1060        StringMaker sm = new StringMaker();
1061
1062        try {
1063            BufferedReader br = new BufferedReader(new StringReader(text));
1064
1065            String s = StringPool.BLANK;
1066
1067            while ((s = br.readLine()) != null) {
1068                if (s.length() == 0) {
1069                    sm.append(lineSeparator);
1070                }
1071                else {
1072                    String[] tokens = s.split(StringPool.SPACE);
1073                    boolean firstWord = true;
1074                    int curLineLength = 0;
1075
1076                    for (int i = 0; i < tokens.length; i++) {
1077                        if (!firstWord) {
1078                            sm.append(StringPool.SPACE);
1079                            curLineLength++;
1080                        }
1081
1082                        if (firstWord) {
1083                            sm.append(lineSeparator);
1084                        }
1085
1086                        sm.append(tokens[i]);
1087
1088                        curLineLength += tokens[i].length();
1089
1090                        if (curLineLength >= width) {
1091                            firstWord = true;
1092                            curLineLength = 0;
1093                        }
1094                        else {
1095                            firstWord = false;
1096                        }
1097                    }
1098                }
1099            }
1100        }
1101        catch (IOException ioe) {
1102            _log.error(ioe.getMessage());
1103        }
1104
1105        return sm.toString();
1106    }
1107
1108    private static boolean _isTrimable(char c, char[] exceptions) {
1109        if ((exceptions != null) && (exceptions.length > 0)) {
1110            for (int i = 0; i < exceptions.length; i++) {
1111                if (c == exceptions[i]) {
1112                    return false;
1113                }
1114            }
1115        }
1116
1117        return Character.isWhitespace(c);
1118    }
1119
1120    private static Log _log = LogFactoryUtil.getLog(StringUtil.class);
1121
1122}