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