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