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 }