View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.commons.util;
20  
21  import java.util.ArrayList;
22  
23  
24  /**
25   * Implements utility functions for the String class
26   *
27   * <p>
28   * Emphasis on performance and reduced memory allocation/garbage collection
29   * in exchange for longer more complex code.
30   * </p>
31   *
32   * @since 1.0.1
33   * @author Anton Koinov (latest modification by $Author: skitching $)
34   * @version $Revision: 673827 $ $Date: 2008-07-03 16:46:23 -0500 (jue, 03 jul 2008) $
35   */
36  public final class StringUtils
37  {
38      private StringUtils()
39      {
40          // utility class, no instantiation
41      }
42  
43      //~ Methods ------------------------------------------------------------------------------------
44  
45      /**
46       * Checks that the string represents a floating point number that CANNOT be
47       * in exponential notation
48       *
49       * @param str the string to check
50       *
51       * @return boolean
52       */
53      public static boolean isFloatNoExponent(String str)
54      {
55          int len = str.length();
56          if (len == 0)
57          {
58              return false;
59          }
60  
61          // skip first char if sign char
62          char c = str.charAt(0);
63          int  i = ((c == '-') || (c == '+')) ? 1 : 0;
64  
65          // is it only a sign?
66          if (i >= len)
67          {
68              return false;
69          }
70  
71          boolean decimalPointFound = false;
72  
73          do
74          {
75              c = str.charAt(i);
76              if (c == '.')
77              {
78                  // is this a second dot?
79                  if (decimalPointFound)
80                  {
81                      return false;
82                  }
83  
84                  decimalPointFound = true;
85              }
86              else if (!Character.isDigit(c))
87              {
88                  return false;
89              }
90  
91              i++;
92          }
93          while (i < len);
94  
95          return true;
96      }
97  
98      public static boolean isFloatWithOptionalExponent(String str)
99      {
100         int len = str.length();
101         if (len == 0)
102         {
103             return false;
104         }
105 
106         // skip first char if sign char
107         char c = str.charAt(0);
108         int  i = ((c == '-') || (c == '+')) ? 1 : 0;
109 
110         // is it only a sign?
111         if (i >= len)
112         {
113             return false;
114         }
115 
116         boolean exponentFound     = false;
117         boolean decimalPointFound = false;
118 
119         do
120         {
121             c = str.charAt(i);
122             switch (c)
123             {
124                 case '.':
125 
126                     // is this a second one, are we in the exponent?
127                     if (decimalPointFound || exponentFound)
128                     {
129                         return false;
130                     }
131                     decimalPointFound = true;
132 
133                     break;
134 
135                 case 'e':
136                 case 'E':
137 
138                     // is this a second one?
139                     if (exponentFound)
140                     {
141                         return false;
142                     }
143                     exponentFound = true;
144 
145                     // check for exponent sign
146                     c = str.charAt(i + 1);
147 
148                     if ((c == '-') || (c == '+'))
149                     {
150                         i++;
151                     }
152 
153                     break;
154 
155                 default:
156                     if (!Character.isDigit(c))
157                     {
158                         return false;
159                     }
160             }
161 
162             i++;
163         }
164         while (i < len);
165 
166         return true;
167     }
168 
169     public static boolean isInteger(String str)
170     {
171         int len = str.length();
172         if (len == 0)
173         {
174             return false;
175         }
176 
177         // skip first char if sign char
178         char c = str.charAt(0);
179         int  i = ((c == '-') || (c == '+')) ? 1 : 0;
180 
181         // is it only a sign?
182         if (i >= len)
183         {
184             return false;
185         }
186 
187         do
188         {
189             if (!Character.isDigit(str.charAt(i)))
190             {
191                 return false;
192             }
193             i++;
194         }
195         while (i < len);
196 
197         return true;
198     }
199 
200     public static boolean isUnsignedInteger(String str)
201     {
202         int len = str.length();
203         if (len == 0)
204         {
205             return false;
206         }
207 
208         for (int i = 0; i < len; i++)
209         {
210             if (!Character.isDigit(str.charAt(i)))
211             {
212                 return false;
213             }
214         }
215 
216         return true;
217     }
218 
219     /**
220      * Undoubles the quotes inside the string <br> Example:<br>
221      * <pre>
222      * hello""world becomes hello"world
223      * </pre>
224      *
225      * @param str input string to dequote
226      * @param quote the quoting char
227      *
228      * @return dequoted string
229      */
230     public static String dequote(String str, char quote)
231     {
232         // Is there anything to dequote?
233         if (str == null)
234         {
235             return null;
236         }
237 
238         return dequote(str, 0, str.length(), quote);
239     }
240 
241     /**
242      * Undoubles the quotes inside a substring <br> Example:<br>
243      * <pre>
244      * hello""world becomes hello"world
245      * </pre>
246      * WARNING: scan for quote may continue to the end of the string, make sure
247      * that either <code>charAt(end + 1) == quote</code> or <code>end =
248      * str.lentgth()</code>. If in doubt call
249      * <code>dequote(str.substring(begin, end), quote)</code>
250      *
251      * @param str input string from which to get the substring, must not be
252      *        null
253      * @param begin begin index for substring
254      * @param end end index for substring
255      * @param quote the quoting char
256      *
257      * @return dequoted string
258      *
259      * @throws IllegalArgumentException if string is incorrectly quoted
260      */
261     public static String dequote(String str, int begin, int end, char quote)
262     {
263         // Is there anything to dequote?
264         if (begin == end)
265         {
266             return "";
267         }
268 
269         int end_ = str.indexOf(quote, begin);
270 
271         // If no quotes, return the original string
272         // and save StringBuffer allocation/char copying
273         if (end_ < 0)
274         {
275             return str.substring(begin, end);
276         }
277 
278         StringBuffer sb     = new StringBuffer(end - begin);
279         int          begin_ = begin; // need begin later
280         for (; (end_ >= 0) && (end_ < end);
281             end_ = str.indexOf(quote, begin_ = end_ + 2))
282         {
283             if (((end_ + 1) >= end) || (str.charAt(end_ + 1) != quote))
284             {
285                 throw new IllegalArgumentException(
286                     "Internal quote not doubled in string '"
287                     + str.substring(begin, end) + "'");
288             }
289 
290             sb.append(substring(str, begin_, end_)).append(quote);
291         }
292 
293         return sb.append(substring(str, begin_, end)).toString();
294     }
295 
296     /**
297      * Removes the surrounding quote and any double quote inside the string <br>
298      * Example:<br>
299      * <pre>
300      * "hello""world" becomes hello"world
301      * </pre>
302      *
303      * @param str input string to dequote
304      * @param quote the quoting char
305      *
306      * @return dequoted String
307      */
308     public static String dequoteFull(String str, char quote)
309     {
310         if (str == null)
311         {
312             return null;
313         }
314 
315         return dequoteFull(str, 0, str.length(), quote);
316     }
317 
318     public static String dequoteFull(String str, int begin, int end, char quote)
319     {
320         // If empty substring, return empty string
321         if (begin == end)
322         {
323             return "";
324         }
325 
326         // If not quoted, return string
327         if (str.charAt(begin) != quote)
328         {
329             return str.substring(begin, end);
330         }
331 
332         int _end = end - 1;
333         if ((str.length() < 2) || (str.charAt(_end) != quote))
334         {
335             throw new IllegalArgumentException(
336                 "Closing quote missing in string '"
337                 + substring(str, begin, end) + "'");
338         }
339 
340         return dequote(str, begin + 1, _end, quote);
341     }
342 
343     public static String replace(String str, String repl, String with)
344     {
345         int lastindex = 0;
346         int pos = str.indexOf(repl);
347 
348         // If no replacement needed, return the original string
349         // and save StringBuffer allocation/char copying
350         if (pos < 0)
351         {
352             return str;
353         }
354 
355         int          len     = repl.length();
356         int          lendiff = with.length() - repl.length();
357         StringBuffer out     =
358             new StringBuffer((lendiff <= 0) ? str.length()
359                 : (str.length() + (10 * lendiff)));
360         for (; pos >= 0; pos = str.indexOf(repl, lastindex = pos + len))
361         {
362             out.append(substring(str, lastindex, pos)).append(with);
363         }
364 
365         return out.append(substring(str, lastindex, str.length())).toString();
366     }
367 
368     public static String replace(String str, char repl, String with)
369     {
370         int pos = str.indexOf(repl);
371 
372         // If no replacement needed, return the original string
373         // and save StringBuffer allocation/char copying
374         if (pos < 0)
375         {
376             return str;
377         }
378 
379         int          len       = str.length();
380         int          lendiff   = with.length() - 1;
381         StringBuffer out       =
382             new StringBuffer((lendiff <= 0) ? str.length()
383                 : (str.length() + (10 * lendiff)));
384         int          lastindex = 0;
385         for (; pos >= 0; pos = str.indexOf(repl, lastindex = pos + 1))
386         {
387             out.append(substring(str, lastindex, pos)).append(with);
388         }
389 
390         return out.append(substring(str, lastindex, len)).toString();
391     }
392 
393     public static StringBuffer replace(
394         StringBuffer out, String s, String repl, String with)
395     {
396         int lastindex = 0;
397         int len = repl.length();
398         for (int index = s.indexOf(repl); index >= 0;
399                     index = s.indexOf(repl, lastindex = index + len))
400         {
401             // we have search string at position index
402             out.append(substring(s, lastindex, index)).append(with);
403         }
404 
405         return out.append(substring(s, lastindex, len));
406     }
407 
408     /**
409      * Split a string into an array of strings arround a character separator.
410      * This  function will be efficient for longer strings
411      *
412      * @param str the string to be split
413      * @param separator the separator character
414      *
415      * @return array of string subparts
416      */
417     public static String[] splitLongString(String str, char separator)
418     {
419         int len;
420         if (str == null || (len = str.length()) == 0)
421         {
422             return ArrayUtils.EMPTY_STRING_ARRAY;
423         }
424 
425         int       oldPos = 0;
426         ArrayList list = new ArrayList();
427         for (
428             int pos = str.indexOf(separator); pos >= 0;
429                     pos = str.indexOf(separator, (oldPos = (pos + 1))))
430         {
431             list.add(substring(str, oldPos, pos));
432         }
433 
434         list.add(substring(str, oldPos, len));
435 
436         return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
437     }
438 
439     /**
440      * Split a string into an array of strings arround a character separator.
441      * Each element can be optionally quoted by the quote character.<br>
442      * This function will be efficient for long strings
443      *
444      * @param str the string to be split
445      * @param separator the separator character
446      * @param quote the quote character
447      *
448      * @return array of string subparts
449      *
450      * @throws IllegalArgumentException DOCUMENT ME!
451      */
452     public static String[] splitLongString(
453         String str, char separator, char quote)
454     {
455         int len;
456         if (str == null || (len = str.length()) == 0)
457         {
458             return ArrayUtils.EMPTY_STRING_ARRAY;
459         }
460 
461         int       oldPos = 0;
462         ArrayList list = new ArrayList();
463         for (int pos = 0; pos < len; oldPos = ++pos)
464         {
465             // Skip quoted text, if any
466             while ((pos < len) && (str.charAt(pos) == quote))
467             {
468                 pos = str.indexOf(quote, pos + 1) + 1;
469 
470                 if (pos == 0)
471                 {
472                     throw new IllegalArgumentException(
473                         "Closing quote missing in string '" + str + "'");
474                 }
475             }
476 
477             boolean quoted;
478 
479             if (pos != oldPos)
480             {
481                 quoted = true;
482 
483                 if ((pos < len) && (str.charAt(pos) != separator))
484                 {
485                     throw new IllegalArgumentException(
486                         "Separator must follow closing quote in string '"
487                         + str + "'");
488                 }
489             }
490             else
491             {
492                 quoted     = false;
493                 pos        = str.indexOf(separator, pos);
494                 if (pos < 0)
495                 {
496                     pos = len;
497                 }
498             }
499 
500             list.add(
501                 quoted ? dequote(str, oldPos + 1, pos - 1, quote)
502                     : substring(str, oldPos, pos));
503         }
504 
505         return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
506     }
507 
508     /**
509      * Split a string into an array of strings arround a character separator.
510      * This  function will be efficient for short strings, for longer strings,
511      * another approach may be better
512      *
513      * @param str the string to be split
514      * @param separator the separator character
515      *
516      * @return array of string subparts
517      */
518     public static String[] splitShortString(String str, char separator)
519     {
520         int len;
521         if (str == null || (len = str.length()) == 0)
522         {
523             return ArrayUtils.EMPTY_STRING_ARRAY;
524         }
525 
526         int lastTokenIndex = 0;
527 
528         // Step 1: how many substrings?
529         //      We exchange double scan time for less memory allocation
530         for (int pos = str.indexOf(separator);
531             pos >= 0; pos = str.indexOf(separator, pos + 1))
532         {
533             lastTokenIndex++;
534         }
535 
536         // Step 2: allocate exact size array
537         String[] list   = new String[lastTokenIndex + 1];
538 
539         int      oldPos = 0;
540 
541         // Step 3: retrieve substrings
542         for (
543             int pos = str.indexOf(separator), i = 0; pos >= 0;
544                     pos = str.indexOf(separator, (oldPos = (pos + 1))))
545         {
546             list[i++] = substring(str, oldPos, pos);
547         }
548 
549         list[lastTokenIndex] = substring(str, oldPos, len);
550 
551         return list;
552     }
553 
554     /**
555      * Split a string into an array of strings arround a character separator.
556      * Each element can be optionally quoted by the quote character.<br>
557      * This function will be efficient for short strings, for longer strings,
558      * another approach may be better
559      *
560      * @param str the string to be split
561      * @param separator the separator character
562      * @param quote the quote character
563      *
564      * @return array of string subparts
565      *
566      * @throws IllegalArgumentException DOCUMENT ME!
567      */
568     public static String[] splitShortString(
569         String str, char separator, char quote)
570     {
571         int len;
572         if (str == null || (len = str.length()) == 0)
573         {
574             return ArrayUtils.EMPTY_STRING_ARRAY;
575         }
576 
577         // Step 1: how many substrings?
578         //      We exchange double scan time for less memory allocation
579         int tokenCount = 0;
580         for (int pos = 0; pos < len; pos++)
581         {
582             tokenCount++;
583 
584             int oldPos = pos;
585 
586             // Skip quoted text, if any
587             while ((pos < len) && (str.charAt(pos) == quote))
588             {
589                 pos = str.indexOf(quote, pos + 1) + 1;
590 
591                 // pos == 0 is not found (-1 returned by indexOf + 1)
592                 if (pos == 0)
593                 {
594                     throw new IllegalArgumentException(
595                         "Closing quote missing in string '" + str + "'");
596                 }
597             }
598 
599             if (pos != oldPos)
600             {
601                 if ((pos < len) && (str.charAt(pos) != separator))
602                 {
603                     throw new IllegalArgumentException(
604                         "Separator must follow closing quote in strng '"
605                         + str + "'");
606                 }
607             }
608             else
609             {
610                 pos = str.indexOf(separator, pos);
611                 if (pos < 0)
612                 {
613                     break;
614                 }
615             }
616         }
617 
618         // Main loop will finish one substring short when last char is separator
619         if (str.charAt(len - 1) == separator)
620         {
621             tokenCount++;
622         }
623 
624         // Step 2: allocate exact size array
625         String[] list = new String[tokenCount];
626 
627         // Step 3: retrieve substrings
628         // Note: on this pass we do not check for correctness,
629         //       since we have already done so
630         tokenCount--; // we want to stop one token short
631 
632         int oldPos = 0;
633         for (int pos = 0, i = 0; i < tokenCount; i++, oldPos = ++pos)
634         {
635             boolean quoted;
636 
637             // Skip quoted text, if any
638             while (str.charAt(pos) == quote)
639             {
640                 pos = str.indexOf(quote, pos + 1) + 1;
641             }
642 
643             if (pos != oldPos)
644             {
645                 quoted = true;
646 
647                 if (str.charAt(pos) != separator)
648                 {
649                     throw new IllegalArgumentException(
650                         "Separator must follow closing quote in strng '"
651                         + str + "'");
652                 }
653             }
654             else
655             {
656                 quoted     = false;
657                 pos        = str.indexOf(separator, pos);
658             }
659 
660             list[i] =
661                 quoted ? dequote(str, oldPos + 1, pos - 1, quote)
662                     : substring(str, oldPos, pos);
663         }
664 
665         list[tokenCount] = dequoteFull(str, oldPos, len, quote);
666 
667         return list;
668     }
669 
670     public static String substring(String str, int begin, int end)
671     {
672         if (begin == end)
673         {
674             return "";
675         }
676 
677         return str.substring(begin, end);
678     }
679 
680     public static String[] trim(String[] strings)
681     {
682         if (strings == null)
683         {
684             return null;
685         }
686 
687         for (int i = 0, len = strings.length; i < len; i++)
688         {
689             strings[i] = strings[i].trim();
690         }
691 
692         return strings;
693     }
694 
695     /**
696      * Returns the minimum index >= 0, if any
697      *
698      * <p>
699      * Use to find the first of two characters in a string:<br>
700      * <code>minIndex(s.indexOf('/'), indexOf('\'))</code>
701      * </p>
702      *
703      */
704     public static int minIndex(int a, int b)
705     {
706         return (a < 0) ? b
707             : (b < 0) ? a
708                 : (a < b) ? a : b;
709     }
710 }