1 package org.apache.maven.shared.utils;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22
23 import java.util.Arrays;
24 import java.util.Iterator;
25 import java.util.Locale;
26 import java.util.Map;
27 import java.util.StringTokenizer;
28
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31
32 /**
33 * <p>Common <code>String</code> manipulation routines.</p>
34 * <p/>
35 * <p>Originally from
36 * <a href="http://jakarta.apache.org/turbine/">Turbine</a>, the
37 * GenerationJavaCore library and Velocity.
38 * Later a lots methods from commons-lang StringUtils
39 * got added too. Gradually smaller additions and fixes have been made
40 * over the time by various ASF committers.</p>
41 *
42 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
43 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
44 * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
45 * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
46 * @author <a href="mailto:ed@apache.org">Ed Korthof</a>
47 * @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a>
48 * @author Stephen Colebourne
49 * @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a>
50 * @author Holger Krauth
51 * @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
52 * @version $Id$
53 *
54 */
55 public class StringUtils
56 {
57 /**
58 * <p><code>StringUtils</code> instances should NOT be constructed in
59 * standard programming. Instead, the class should be used as
60 * <code>StringUtils.trim(" foo ");</code>.</p>
61 * <p/>
62 * <p>This constructor is public to permit tools that require a JavaBean
63 * manager to operate.</p>
64 */
65 public StringUtils()
66 {
67 }
68
69 // Empty
70 //--------------------------------------------------------------------------
71
72 /**
73 * <p>Removes control characters, including whitespace, from both
74 * ends of this String, handling <code>null</code> by returning
75 * an empty String.</p>
76 *
77 * @param str the String to check
78 * @return the trimmed text (never <code>null</code>)
79 * @see java.lang.String#trim()
80 */
81 @Nonnull public static String clean( String str )
82 {
83 return ( str == null ? "" : str.trim() );
84 }
85
86 /**
87 * <p>Removes control characters, including whitespace, from both
88 * ends of this String, handling <code>null</code> by returning
89 * <code>null</code>.</p>
90 *
91 * @param str the String to check
92 * @return the trimmed text (or <code>null</code>)
93 * @see java.lang.String#trim()
94 */
95 public static String trim( String str )
96 {
97 return ( str == null ? null : str.trim() );
98 }
99
100 /**
101 * <p>Deletes all whitespaces from a String.</p>
102 * <p/>
103 * <p>Whitespace is defined by
104 * {@link Character#isWhitespace(char)}.</p>
105 *
106 * @param str String target to delete whitespace from
107 * @return the String without whitespaces
108 * @throws NullPointerException
109 */
110 @Nonnull public static String deleteWhitespace( @Nonnull String str )
111 {
112 StringBuilder buffer = new StringBuilder();
113 int sz = str.length();
114 for ( int i = 0; i < sz; i++ )
115 {
116 if ( !Character.isWhitespace( str.charAt( i ) ) )
117 {
118 buffer.append( str.charAt( i ) );
119 }
120 }
121 return buffer.toString();
122 }
123
124 /**
125 * <p>Checks if a String is non <code>null</code> and is
126 * not empty (<code>length > 0</code>).</p>
127 *
128 * @param str the String to check
129 * @return true if the String is non-null, and not length zero
130 */
131 public static boolean isNotEmpty( @Nullable String str )
132 {
133 return ( ( str != null ) && ( str.length() > 0 ) );
134 }
135
136 /**
137 * <p>Checks if a (trimmed) String is <code>null</code> or empty.</p>
138 * <p/>
139 * <p><strong>Note:</strong> In future releases, this method will no longer trim the input string such that it works
140 * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be
141 * migrated to use {@link #isBlank(String)} instead.</p>
142 *
143 * @param str the String to check
144 * @return <code>true</code> if the String is <code>null</code>, or
145 * length zero once trimmed
146 */
147 public static boolean isEmpty( @Nullable String str )
148 {
149 return ( ( str == null ) || ( str.trim().length() == 0 ) );
150 }
151
152 /**
153 * <p>
154 * Checks if a String is whitespace, empty ("") or null.
155 * </p>
156 * <p/>
157 * <pre>
158 * StringUtils.isBlank(null) = true
159 * StringUtils.isBlank("") = true
160 * StringUtils.isBlank(" ") = true
161 * StringUtils.isBlank("bob") = false
162 * StringUtils.isBlank(" bob ") = false
163 * </pre>
164 *
165 * @param str the String to check, may be null
166 * @return <code>true</code> if the String is null, empty or whitespace
167 *
168 */
169 public static boolean isBlank( @Nullable String str )
170 {
171 int strLen;
172 // CHECKSTYLE_OFF: InnerAssignment
173 if ( str == null || ( strLen = str.length() ) == 0 )
174 // CHECKSTYLE_ON: InnerAssignment
175 {
176 return true;
177 }
178 for ( int i = 0; i < strLen; i++ )
179 {
180 if ( !Character.isWhitespace( str.charAt( i ) ) )
181 {
182 return false;
183 }
184 }
185 return true;
186 }
187
188 /**
189 * <p>
190 * Checks if a String is not empty (""), not null and not whitespace only.
191 * </p>
192 * <p/>
193 * <pre>
194 * StringUtils.isNotBlank(null) = false
195 * StringUtils.isNotBlank("") = false
196 * StringUtils.isNotBlank(" ") = false
197 * StringUtils.isNotBlank("bob") = true
198 * StringUtils.isNotBlank(" bob ") = true
199 * </pre>
200 *
201 * @param str the String to check, may be null
202 * @return <code>true</code> if the String is not empty and not null and not whitespace
203 *
204 */
205 public static boolean isNotBlank( @Nullable String str )
206 {
207 return !isBlank( str );
208 }
209
210 // Equals and IndexOf
211 //--------------------------------------------------------------------------
212
213 /**
214 * <p>Compares two Strings, returning <code>true</code> if they are equal.</p>
215 * <p/>
216 * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
217 * references are considered to be equal. The comparison is case sensitive.</p>
218 *
219 * @param str1 the first string
220 * @param str2 the second string
221 * @return <code>true</code> if the Strings are equal, case sensitive, or
222 * both <code>null</code>
223 * @see java.lang.String#equals(Object)
224 */
225 public static boolean equals( @Nullable String str1, @Nullable String str2 )
226 {
227 return ( str1 == null ? str2 == null : str1.equals( str2 ) );
228 }
229
230 /**
231 * <p>Compares two Strings, returning <code>true</code> if they are equal ignoring
232 * the case.</p>
233 * <p/>
234 * <p><code>Nulls</code> are handled without exceptions. Two <code>null</code>
235 * references are considered equal. Comparison is case insensitive.</p>
236 *
237 * @param str1 the first string
238 * @param str2 the second string
239 * @return <code>true</code> if the Strings are equal, case insensitive, or
240 * both <code>null</code>
241 * @see java.lang.String#equalsIgnoreCase(String)
242 */
243 public static boolean equalsIgnoreCase( String str1, String str2 )
244 {
245 return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) );
246 }
247
248 /**
249 * <p>Find the first index of any of a set of potential substrings.</p>
250 * <p/>
251 * <p><code>null</code> String will return <code>-1</code>.</p>
252 *
253 * @param str the String to check
254 * @param searchStrs the Strings to search for
255 * @return the first index of any of the searchStrs in str
256 * @throws NullPointerException if any of searchStrs[i] is <code>null</code>
257 */
258 public static int indexOfAny( String str, String... searchStrs )
259 {
260 if ( ( str == null ) || ( searchStrs == null ) )
261 {
262 return -1;
263 }
264 // String's can't have a MAX_VALUEth index.
265 int ret = Integer.MAX_VALUE;
266
267 int tmp;
268 for ( String searchStr : searchStrs )
269 {
270 tmp = str.indexOf( searchStr );
271 if ( tmp == -1 )
272 {
273 continue;
274 }
275
276 if ( tmp < ret )
277 {
278 ret = tmp;
279 }
280 }
281
282 return ( ret == Integer.MAX_VALUE ) ? -1 : ret;
283 }
284
285 /**
286 * <p>Find the latest index of any of a set of potential substrings.</p>
287 * <p/>
288 * <p><code>null</code> string will return <code>-1</code>.</p>
289 *
290 * @param str the String to check
291 * @param searchStrs the Strings to search for
292 * @return the last index of any of the Strings
293 * @throws NullPointerException if any of searchStrs[i] is <code>null</code>
294 */
295 public static int lastIndexOfAny( String str, String... searchStrs )
296 {
297 if ( ( str == null ) || ( searchStrs == null ) )
298 {
299 return -1;
300 }
301 int ret = -1;
302 int tmp;
303 for ( String searchStr : searchStrs )
304 {
305 tmp = str.lastIndexOf( searchStr );
306 if ( tmp > ret )
307 {
308 ret = tmp;
309 }
310 }
311 return ret;
312 }
313
314 // Substring
315 //--------------------------------------------------------------------------
316
317 /**
318 * <p>Gets a substring from the specified string avoiding exceptions.</p>
319 * <p/>
320 * <p>A negative start position can be used to start <code>n</code>
321 * characters from the end of the String.</p>
322 *
323 * @param str the String to get the substring from
324 * @param start the position to start from, negative means
325 * count back from the end of the String by this many characters
326 * @return substring from start position
327 */
328 public static String substring( String str, int start )
329 {
330 if ( str == null )
331 {
332 return null;
333 }
334
335 // handle negatives, which means last n characters
336 if ( start < 0 )
337 {
338 start = str.length() + start; // remember start is negative
339 }
340
341 if ( start < 0 )
342 {
343 start = 0;
344 }
345 if ( start > str.length() )
346 {
347 return "";
348 }
349
350 return str.substring( start );
351 }
352
353 /**
354 * <p>Gets a substring from the specified String avoiding exceptions.</p>
355 * <p/>
356 * <p>A negative start position can be used to start/end <code>n</code>
357 * characters from the end of the String.</p>
358 *
359 * @param str the String to get the substring from
360 * @param start the position to start from, negative means
361 * count back from the end of the string by this many characters
362 * @param end the position to end at (exclusive), negative means
363 * count back from the end of the String by this many characters
364 * @return substring from start position to end positon
365 */
366 public static String substring( String str, int start, int end )
367 {
368 if ( str == null )
369 {
370 return null;
371 }
372
373 // handle negatives
374 if ( end < 0 )
375 {
376 end = str.length() + end; // remember end is negative
377 }
378 if ( start < 0 )
379 {
380 start = str.length() + start; // remember start is negative
381 }
382
383 // check length next
384 if ( end > str.length() )
385 {
386 // check this works.
387 end = str.length();
388 }
389
390 // if start is greater than end, return ""
391 if ( start > end )
392 {
393 return "";
394 }
395
396 if ( start < 0 )
397 {
398 start = 0;
399 }
400 if ( end < 0 )
401 {
402 end = 0;
403 }
404
405 return str.substring( start, end );
406 }
407
408 /**
409 * <p>Gets the leftmost <code>n</code> characters of a String.</p>
410 * <p/>
411 * <p>If <code>n</code> characters are not available, or the
412 * String is <code>null</code>, the String will be returned without
413 * an exception.</p>
414 *
415 * @param str the String to get the leftmost characters from
416 * @param len the length of the required String
417 * @return the leftmost characters
418 * @throws IllegalArgumentException if len is less than zero
419 */
420 public static String left( String str, int len )
421 {
422 if ( len < 0 )
423 {
424 throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
425 }
426 if ( ( str == null ) || ( str.length() <= len ) )
427 {
428 return str;
429 }
430 else
431 {
432 return str.substring( 0, len );
433 }
434 }
435
436 /**
437 * <p>Gets the rightmost <code>n</code> characters of a String.</p>
438 * <p/>
439 * <p>If <code>n</code> characters are not available, or the String
440 * is <code>null</code>, the String will be returned without an
441 * exception.</p>
442 *
443 * @param str the String to get the rightmost characters from
444 * @param len the length of the required String
445 * @return the leftmost characters
446 * @throws IllegalArgumentException if len is less than zero
447 */
448 public static String right( String str, int len )
449 {
450 if ( len < 0 )
451 {
452 throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
453 }
454 if ( ( str == null ) || ( str.length() <= len ) )
455 {
456 return str;
457 }
458 else
459 {
460 return str.substring( str.length() - len );
461 }
462 }
463
464 /**
465 * <p>Gets <code>n</code> characters from the middle of a String.</p>
466 * <p/>
467 * <p>If <code>n</code> characters are not available, the remainder
468 * of the String will be returned without an exception. If the
469 * String is <code>null</code>, <code>null</code> will be returned.</p>
470 *
471 * @param str the String to get the characters from
472 * @param pos the position to start from
473 * @param len the length of the required String
474 * @return the leftmost characters
475 * @throws IndexOutOfBoundsException if pos is out of bounds
476 * @throws IllegalArgumentException if len is less than zero
477 */
478 public static String mid( String str, int pos, int len )
479 {
480 if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) )
481 {
482 throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" );
483 }
484 if ( len < 0 )
485 {
486 throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" );
487 }
488 if ( str == null )
489 {
490 return null;
491 }
492 if ( str.length() <= ( pos + len ) )
493 {
494 return str.substring( pos );
495 }
496 else
497 {
498 return str.substring( pos, pos + len );
499 }
500 }
501
502 // Splitting
503 //--------------------------------------------------------------------------
504
505 /**
506 * <p>Splits the provided text into a array, using whitespace as the
507 * separator.</p>
508 * <p/>
509 * <p>The separator is not included in the returned String array.</p>
510 *
511 * @param str the String to parse
512 * @return an array of parsed Strings
513 */
514 @Nonnull public static String[] split( @Nonnull String str )
515 {
516 return split( str, null, -1 );
517 }
518
519 /**
520 * @param text The text to be split.
521 * @param separator The separator at which the text will be split.
522 * @return The resulting array.
523 * @see #split(String, String, int)
524 */
525 @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator )
526 {
527 return split( text, separator, -1 );
528 }
529
530 /**
531 * <p>Splits the provided text into a array, based on a given separator.</p>
532 * <p/>
533 * <p>The separator is not included in the returned String array. The
534 * maximum number of splits to perfom can be controlled. A <code>null</code>
535 * separator will cause parsing to be on whitespace.</p>
536 * <p/>
537 * <p>This is useful for quickly splitting a String directly into
538 * an array of tokens, instead of an enumeration of tokens (as
539 * <code>StringTokenizer</code> does).</p>
540 *
541 * @param str The string to parse.
542 * @param separator Characters used as the delimiters. If
543 * <code>null</code>, splits on whitespace.
544 * @param max The maximum number of elements to include in the
545 * array. A zero or negative value implies no limit.
546 * @return an array of parsed Strings
547 */
548 @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max )
549 {
550 StringTokenizer tok;
551 if ( separator == null )
552 {
553 // Null separator means we're using StringTokenizer's default
554 // delimiter, which comprises all whitespace characters.
555 tok = new StringTokenizer( str );
556 }
557 else
558 {
559 tok = new StringTokenizer( str, separator );
560 }
561
562 int listSize = tok.countTokens();
563 if ( ( max > 0 ) && ( listSize > max ) )
564 {
565 listSize = max;
566 }
567
568 String[] list = new String[listSize];
569 int i = 0;
570 int lastTokenBegin;
571 int lastTokenEnd = 0;
572 while ( tok.hasMoreTokens() )
573 {
574 if ( ( max > 0 ) && ( i == listSize - 1 ) )
575 {
576 // In the situation where we hit the max yet have
577 // tokens left over in our input, the last list
578 // element gets all remaining text.
579 String endToken = tok.nextToken();
580 lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
581 list[i] = str.substring( lastTokenBegin );
582 break;
583 }
584 else
585 {
586 list[i] = tok.nextToken();
587 lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
588 lastTokenEnd = lastTokenBegin + list[i].length();
589 }
590 i++;
591 }
592 return list;
593 }
594
595 // Joining
596 //--------------------------------------------------------------------------
597
598 /**
599 * <p>Concatenates elements of an array into a single String.</p>
600 * <p/>
601 * <p>The difference from join is that concatenate has no delimiter.</p>
602 *
603 * @param array the array of values to concatenate.
604 * @return the concatenated string.
605 */
606 @Nonnull public static String concatenate( @Nonnull Object... array )
607 {
608 return join( array, "" );
609 }
610
611 /**
612 * <p>Joins the elements of the provided array into a single String
613 * containing the provided list of elements.</p>
614 * <p/>
615 * <p>No delimiter is added before or after the list. A
616 * <code>null</code> separator is the same as a blank String.</p>
617 *
618 * @param array the array of values to join together
619 * @param separator the separator character to use
620 * @return the joined String
621 */
622 @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator )
623 {
624 if ( separator == null )
625 {
626 separator = "";
627 }
628 int arraySize = array.length;
629 int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize );
630 StringBuilder buf = new StringBuilder( bufSize );
631
632 for ( int i = 0; i < arraySize; i++ )
633 {
634 if ( i > 0 )
635 {
636 buf.append( separator );
637 }
638 buf.append( array[i] );
639 }
640 return buf.toString();
641 }
642
643 /**
644 * <p>Joins the elements of the provided <code>Iterator</code> into
645 * a single String containing the provided elements.</p>
646 * <p/>
647 * <p>No delimiter is added before or after the list. A
648 * <code>null</code> separator is the same as a blank String.</p>
649 *
650 * @param iterator the <code>Iterator</code> of values to join together
651 * @param separator the separator character to use
652 * @return the joined String
653 */
654 @Nonnull public static String join( @Nonnull Iterator<?> iterator, String separator )
655 {
656 if ( separator == null )
657 {
658 separator = "";
659 }
660 StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small
661 while ( iterator.hasNext() )
662 {
663 buf.append( iterator.next() );
664 if ( iterator.hasNext() )
665 {
666 buf.append( separator );
667 }
668 }
669 return buf.toString();
670 }
671
672 // Replacing
673 //--------------------------------------------------------------------------
674
675 /**
676 * <p>Replace a char with another char inside a larger String, once.</p>
677 * <p/>
678 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
679 *
680 * @param text text to search and replace in
681 * @param repl char to search for
682 * @param with char to replace with
683 * @return the text with any replacements processed
684 * @see #replace(String text, char repl, char with, int max)
685 */
686 public static String replaceOnce( @Nullable String text, char repl, char with )
687 {
688 return replace( text, repl, with, 1 );
689 }
690
691 /**
692 * <p>Replace all occurances of a char within another char.</p>
693 * <p/>
694 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
695 *
696 * @param text text to search and replace in
697 * @param repl char to search for
698 * @param with char to replace with
699 * @return the text with any replacements processed
700 * @see #replace(String text, char repl, char with, int max)
701 */
702 public static String replace( @Nullable String text, char repl, char with )
703 {
704 return replace( text, repl, with, -1 );
705 }
706
707 /**
708 * <p>Replace a char with another char inside a larger String,
709 * for the first <code>max</code> values of the search char.</p>
710 * <p/>
711 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
712 *
713 * @param text text to search and replace in
714 * @param repl char to search for
715 * @param with char to replace with
716 * @param max maximum number of values to replace, or <code>-1</code> if no maximum
717 * @return the text with any replacements processed
718 */
719 public static String replace( @Nullable String text, char repl, char with, int max )
720 {
721 return replace( text, String.valueOf( repl ), String.valueOf( with ), max );
722 }
723
724 /**
725 * <p>Replace a String with another String inside a larger String, once.</p>
726 * <p/>
727 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
728 *
729 * @param text text to search and replace in
730 * @param repl String to search for
731 * @param with String to replace with
732 * @return the text with any replacements processed
733 * @see #replace(String text, String repl, String with, int max)
734 */
735 public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with )
736 {
737 return replace( text, repl, with, 1 );
738 }
739
740 /**
741 * <p>Replace all occurances of a String within another String.</p>
742 * <p/>
743 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
744 *
745 * @param text text to search and replace in
746 * @param repl String to search for
747 * @param with String to replace with
748 * @return the text with any replacements processed
749 * @see #replace(String text, String repl, String with, int max)
750 */
751 public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with )
752 {
753 return replace( text, repl, with, -1 );
754 }
755
756 /**
757 * <p>Replace a String with another String inside a larger String,
758 * for the first <code>max</code> values of the search String.</p>
759 * <p/>
760 * <p>A <code>null</code> reference passed to this method is a no-op.</p>
761 *
762 * @param text text to search and replace in
763 * @param repl String to search for
764 * @param with String to replace with
765 * @param max maximum number of values to replace, or <code>-1</code> if no maximum
766 * @return the text with any replacements processed
767 */
768 public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max )
769 {
770 if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) )
771 {
772 return text;
773 }
774
775 StringBuilder buf = new StringBuilder( text.length() );
776 int start = 0, end;
777 while ( ( end = text.indexOf( repl, start ) ) != -1 )
778 {
779 buf.append( text, start, end ).append( with );
780 start = end + repl.length();
781
782 if ( --max == 0 )
783 {
784 break;
785 }
786 }
787 buf.append( text, start, text.length() );
788 return buf.toString();
789 }
790
791 /**
792 * <p>Overlay a part of a String with another String.</p>
793 *
794 * @param text String to do overlaying in
795 * @param overlay String to overlay
796 * @param start int to start overlaying at
797 * @param end int to stop overlaying before
798 * @return String with overlayed text
799 * @throws NullPointerException if text or overlay is <code>null</code>
800 */
801 @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end )
802 {
803 if ( overlay == null )
804 {
805 throw new NullPointerException( "overlay is null" );
806 }
807 return new StringBuilder( start + overlay.length() + text.length() - end + 1 )
808 .append( text, 0, start )
809 .append( overlay )
810 .append( text, end, text.length() )
811 .toString();
812 }
813
814 // Centering
815 //--------------------------------------------------------------------------
816
817 /**
818 * <p>Center a String in a larger String of size <code>n</code>.<p>
819 * <p/>
820 * <p>Uses spaces as the value to buffer the String with.
821 * Equivalent to <code>center(str, size, " ")</code>.</p>
822 *
823 * @param str String to center
824 * @param size int size of new String
825 * @return String containing centered String
826 * @throws NullPointerException if str is <code>null</code>
827 */
828 @Nonnull public static String center( @Nonnull String str, int size )
829 {
830 return center( str, size, " " );
831 }
832
833 /**
834 * <p>Center a String in a larger String of size <code>n</code>.</p>
835 * <p/>
836 * <p>Uses a supplied String as the value to buffer the String with.</p>
837 *
838 * @param str String to center
839 * @param size int size of new String
840 * @param delim String to buffer the new String with
841 * @return String containing centered String
842 * @throws NullPointerException if str or delim is <code>null</code>
843 * @throws ArithmeticException if delim is the empty String
844 */
845 @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim )
846 {
847 int sz = str.length();
848 int p = size - sz;
849 if ( p < 1 )
850 {
851 return str;
852 }
853 str = leftPad( str, sz + p / 2, delim );
854 str = rightPad( str, size, delim );
855 return str;
856 }
857
858 // Chomping
859 //--------------------------------------------------------------------------
860
861 /**
862 * <p>Remove the last newline, and everything after it from a String.</p>
863 *
864 * @param str String to chomp the newline from
865 * @return String without chomped newline
866 * @throws NullPointerException if str is <code>null</code>
867 */
868 @Nonnull public static String chomp( @Nonnull String str )
869 {
870 return chomp( str, "\n" );
871 }
872
873 /**
874 * <p>Remove the last value of a supplied String, and everything after
875 * it from a String.</p>
876 *
877 * @param str String to chomp from
878 * @param sep String to chomp
879 * @return String without chomped ending
880 * @throws NullPointerException if str or sep is <code>null</code>
881 */
882 @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep )
883 {
884 int idx = str.lastIndexOf( sep );
885 if ( idx != -1 )
886 {
887 return str.substring( 0, idx );
888 }
889 else
890 {
891 return str;
892 }
893 }
894
895 /**
896 * <p>Remove a newline if and only if it is at the end
897 * of the supplied String.</p>
898 *
899 * @param str String to chomp from
900 * @return String without chomped ending
901 * @throws NullPointerException if str is <code>null</code>
902 */
903 @Nonnull public static String chompLast( @Nonnull String str )
904 {
905 return chompLast( str, "\n" );
906 }
907
908 /**
909 * <p>Remove a value if and only if the String ends with that value.</p>
910 *
911 * @param str String to chomp from
912 * @param sep String to chomp
913 * @return String without chomped ending
914 * @throws NullPointerException if str or sep is <code>null</code>
915 */
916 @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep )
917 {
918 if ( str.length() == 0 )
919 {
920 return str;
921 }
922 String sub = str.substring( str.length() - sep.length() );
923 if ( sep.equals( sub ) )
924 {
925 return str.substring( 0, str.length() - sep.length() );
926 }
927 else
928 {
929 return str;
930 }
931 }
932
933 /**
934 * <p>Remove everything and return the last value of a supplied String, and
935 * everything after it from a String.</p>
936 *
937 * @param str String to chomp from
938 * @param sep String to chomp
939 * @return String chomped
940 * @throws NullPointerException if str or sep is <code>null</code>
941 */
942 @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep )
943 {
944 int idx = str.lastIndexOf( sep );
945 if ( idx == str.length() - sep.length() )
946 {
947 return sep;
948 }
949 else if ( idx != -1 )
950 {
951 return str.substring( idx );
952 }
953 else
954 {
955 return "";
956 }
957 }
958
959 /**
960 * <p>Remove the first value of a supplied String, and everything before it
961 * from a String.</p>
962 *
963 * @param str String to chomp from
964 * @param sep String to chomp
965 * @return String without chomped beginning
966 * @throws NullPointerException if str or sep is <code>null</code>
967 */
968 @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep )
969 {
970 int idx = str.indexOf( sep );
971 if ( idx != -1 )
972 {
973 return str.substring( idx + sep.length() );
974 }
975 else
976 {
977 return str;
978 }
979 }
980
981 /**
982 * <p>Remove and return everything before the first value of a
983 * supplied String from another String.</p>
984 *
985 * @param str String to chomp from
986 * @param sep String to chomp
987 * @return String prechomped
988 * @throws NullPointerException if str or sep is <code>null</code>
989 */
990 @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep )
991 {
992 int idx = str.indexOf( sep );
993 if ( idx != -1 )
994 {
995 return str.substring( 0, idx + sep.length() );
996 }
997 else
998 {
999 return "";
1000 }
1001 }
1002
1003 // Chopping
1004 //--------------------------------------------------------------------------
1005
1006 /**
1007 * <p>Remove the last character from a String.</p>
1008 * <p/>
1009 * <p>If the String ends in <code>\r\n</code>, then remove both
1010 * of them.</p>
1011 *
1012 * @param str String to chop last character from
1013 * @return String without last character
1014 * @throws NullPointerException if str is <code>null</code>
1015 */
1016 @Nonnull public static String chop( @Nonnull String str )
1017 {
1018 if ( "".equals( str ) )
1019 {
1020 return "";
1021 }
1022 if ( str.length() == 1 )
1023 {
1024 return "";
1025 }
1026 int lastIdx = str.length() - 1;
1027 String ret = str.substring( 0, lastIdx );
1028 char last = str.charAt( lastIdx );
1029 if ( last == '\n' )
1030 {
1031 if ( ret.charAt( lastIdx - 1 ) == '\r' )
1032 {
1033 return ret.substring( 0, lastIdx - 1 );
1034 }
1035 }
1036 return ret;
1037 }
1038
1039 /**
1040 * <p>Remove <code>\n</code> from end of a String if it's there.
1041 * If a <code>\r</code> precedes it, then remove that too.</p>
1042 *
1043 * @param str String to chop a newline from
1044 * @return String without newline
1045 * @throws NullPointerException if str is <code>null</code>
1046 */
1047 @Nonnull public static String chopNewline( @Nonnull String str )
1048 {
1049 int lastIdx = str.length() - 1;
1050 char last = str.charAt( lastIdx );
1051 if ( last == '\n' )
1052 {
1053 if ( str.charAt( lastIdx - 1 ) == '\r' )
1054 {
1055 lastIdx--;
1056 }
1057 }
1058 else
1059 {
1060 lastIdx++;
1061 }
1062 return str.substring( 0, lastIdx );
1063 }
1064
1065 // Conversion
1066 //--------------------------------------------------------------------------
1067
1068 // spec 3.10.6
1069
1070 /**
1071 * <p>Escapes any values it finds into their String form.</p>
1072 * <p/>
1073 * <p>So a tab becomes the characters <code>'\\'</code> and
1074 * <code>'t'</code>.</p>
1075 *
1076 * @param str String to escape values in
1077 * @return String with escaped values
1078 * @throws NullPointerException if str is <code>null</code>
1079 */
1080 @Nonnull public static String escape( @Nonnull String str )
1081 {
1082 // improved with code from cybertiger@cyberiantiger.org
1083 // unicode from him, and defaul for < 32's.
1084 int sz = str.length();
1085 StringBuilder buffer = new StringBuilder( 2 * sz );
1086 for ( int i = 0; i < sz; i++ )
1087 {
1088 char ch = str.charAt( i );
1089
1090 // handle unicode
1091 // CHECKSTYLE_OFF: MagicNumber
1092 if ( ch > 0xfff )
1093 {
1094 buffer.append( "\\u" ).append( Integer.toHexString( ch ) );
1095 }
1096 else if ( ch > 0xff )
1097 {
1098 buffer.append( "\\u0" ).append( Integer.toHexString( ch ) );
1099 }
1100 else if ( ch > 0x7f )
1101 {
1102 buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
1103 }
1104 // CHECKSTYLE_ON: MagicNumber
1105 else if ( ch < 32 )
1106 {
1107 switch ( ch )
1108 {
1109 case '\b':
1110 buffer.append( '\\' );
1111 buffer.append( 'b' );
1112 break;
1113 case '\n':
1114 buffer.append( '\\' );
1115 buffer.append( 'n' );
1116 break;
1117 case '\t':
1118 buffer.append( '\\' );
1119 buffer.append( 't' );
1120 break;
1121 case '\f':
1122 buffer.append( '\\' );
1123 buffer.append( 'f' );
1124 break;
1125 case '\r':
1126 buffer.append( '\\' );
1127 buffer.append( 'r' );
1128 break;
1129 default:
1130 if ( ch > 0xf )
1131 {
1132 buffer.append( "\\u00" ).append( Integer.toHexString( ch ) );
1133 }
1134 else
1135 {
1136 buffer.append( "\\u000" ).append( Integer.toHexString( ch ) );
1137 }
1138 break;
1139 }
1140 }
1141 else
1142 {
1143 switch ( ch )
1144 {
1145 case '\'':
1146 buffer.append( '\\' );
1147 buffer.append( '\'' );
1148 break;
1149 case '"':
1150 buffer.append( '\\' );
1151 buffer.append( '"' );
1152 break;
1153 case '\\':
1154 buffer.append( '\\' );
1155 buffer.append( '\\' );
1156 break;
1157 default:
1158 buffer.append( ch );
1159 break;
1160 }
1161 }
1162 }
1163 return buffer.toString();
1164 }
1165
1166 // Padding
1167 //--------------------------------------------------------------------------
1168
1169 /**
1170 * <p>Repeat a String <code>n</code> times to form a
1171 * new string.</p>
1172 *
1173 * @param str String to repeat
1174 * @param repeat number of times to repeat str
1175 * @return String with repeated String
1176 * @throws NegativeArraySizeException if <code>repeat < 0</code>
1177 * @throws NullPointerException if str is <code>null</code>
1178 */
1179 @Nonnull public static String repeat( @Nonnull String str, int repeat )
1180 {
1181 StringBuilder buffer = new StringBuilder( repeat * str.length() );
1182 for ( int i = 0; i < repeat; i++ )
1183 {
1184 buffer.append( str );
1185 }
1186 return buffer.toString();
1187 }
1188
1189 /**
1190 * <p>Right pad a String with spaces.</p>
1191 * <p/>
1192 * <p>The String is padded to the size of <code>n</code>.</p>
1193 *
1194 * @param str String to repeat
1195 * @param size number of times to repeat str
1196 * @return right padded String
1197 * @throws NullPointerException if str is <code>null</code>
1198 */
1199 @Nonnull public static String rightPad( @Nonnull String str, int size )
1200 {
1201 return rightPad( str, size, " " );
1202 }
1203
1204 /**
1205 * <p>Right pad a String with a specified string.</p>
1206 * <p/>
1207 * <p>The String is padded to the size of <code>n</code>.</p>
1208 *
1209 * @param str String to pad out
1210 * @param size size to pad to
1211 * @param delim String to pad with
1212 * @return right padded String
1213 * @throws NullPointerException if str or delim is <code>null</code>
1214 * @throws ArithmeticException if delim is the empty String
1215 */
1216 @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim )
1217 {
1218 size = ( size - str.length() ) / delim.length();
1219 if ( size > 0 )
1220 {
1221 str += repeat( delim, size );
1222 }
1223 return str;
1224 }
1225
1226 /**
1227 * <p>Left pad a String with spaces.</p>
1228 * <p/>
1229 * <p>The String is padded to the size of <code>n</code>.</p>
1230 *
1231 * @param str String to pad out
1232 * @param size size to pad to
1233 * @return left padded String
1234 * @throws NullPointerException if str or delim is <code>null</code>
1235 */
1236 @Nonnull public static String leftPad( @Nonnull String str, int size )
1237 {
1238 return leftPad( str, size, " " );
1239 }
1240
1241 /**
1242 * Left pad a String with a specified string. Pad to a size of n.
1243 *
1244 * @param str String to pad out
1245 * @param size size to pad to
1246 * @param delim String to pad with
1247 * @return left padded String
1248 * @throws NullPointerException if str or delim is null
1249 * @throws ArithmeticException if delim is the empty string
1250 */
1251 @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim )
1252 {
1253 size = ( size - str.length() ) / delim.length();
1254 if ( size > 0 )
1255 {
1256 str = repeat( delim, size ) + str;
1257 }
1258 return str;
1259 }
1260
1261 // Stripping
1262 //--------------------------------------------------------------------------
1263
1264 /**
1265 * <p>Remove whitespace from the front and back of a String.</p>
1266 *
1267 * @param str the String to remove whitespace from
1268 * @return the stripped String
1269 */
1270 public static String strip( String str )
1271 {
1272 return strip( str, null );
1273 }
1274
1275 /**
1276 * <p>Remove a specified String from the front and back of a
1277 * String.</p>
1278 * <p/>
1279 * <p>If whitespace is wanted to be removed, used the
1280 * {@link #strip(java.lang.String)} method.</p>
1281 *
1282 * @param str the String to remove a string from
1283 * @param delim the String to remove at start and end
1284 * @return the stripped String
1285 */
1286 public static String strip( String str, @Nullable String delim )
1287 {
1288 str = stripStart( str, delim );
1289 return stripEnd( str, delim );
1290 }
1291
1292 /**
1293 * <p>Strip whitespace from the front and back of every String
1294 * in the array.</p>
1295 *
1296 * @param strs the Strings to remove whitespace from
1297 * @return the stripped Strings
1298 */
1299 public static String[] stripAll( String... strs )
1300 {
1301 return stripAll( strs, null );
1302 }
1303
1304 /**
1305 * <p>Strip the specified delimiter from the front and back of
1306 * every String in the array.</p>
1307 *
1308 * @param strs the Strings to remove a String from
1309 * @param delimiter the String to remove at start and end
1310 * @return the stripped Strings
1311 */
1312 public static String[] stripAll( String[] strs, @Nullable String delimiter )
1313 {
1314 if ( ( strs == null ) || ( strs.length == 0 ) )
1315 {
1316 return strs;
1317 }
1318 int sz = strs.length;
1319 String[] newArr = new String[sz];
1320 for ( int i = 0; i < sz; i++ )
1321 {
1322 newArr[i] = strip( strs[i], delimiter );
1323 }
1324 return newArr;
1325 }
1326
1327 /**
1328 * <p>Strip any of a supplied String from the end of a String.</p>
1329 * <p/>
1330 * <p>If the strip String is <code>null</code>, whitespace is
1331 * stripped.</p>
1332 *
1333 * @param str the String to remove characters from
1334 * @param strip the String to remove
1335 * @return the stripped String
1336 */
1337 public static String stripEnd( String str, @Nullable String strip )
1338 {
1339 if ( str == null )
1340 {
1341 return null;
1342 }
1343 int end = str.length();
1344
1345 if ( strip == null )
1346 {
1347 while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) )
1348 {
1349 end--;
1350 }
1351 }
1352 else
1353 {
1354 while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) )
1355 {
1356 end--;
1357 }
1358 }
1359 return str.substring( 0, end );
1360 }
1361
1362 /**
1363 * <p>Strip any of a supplied String from the start of a String.</p>
1364 * <p/>
1365 * <p>If the strip String is <code>null</code>, whitespace is
1366 * stripped.</p>
1367 *
1368 * @param str the String to remove characters from
1369 * @param strip the String to remove
1370 * @return the stripped String
1371 */
1372 public static String stripStart( String str, @Nullable String strip )
1373 {
1374 if ( str == null )
1375 {
1376 return null;
1377 }
1378
1379 int start = 0;
1380
1381 int sz = str.length();
1382
1383 if ( strip == null )
1384 {
1385 while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) )
1386 {
1387 start++;
1388 }
1389 }
1390 else
1391 {
1392 while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) )
1393 {
1394 start++;
1395 }
1396 }
1397 return str.substring( start );
1398 }
1399
1400 // Case conversion
1401 //--------------------------------------------------------------------------
1402
1403 /**
1404 * <p>Convert a String to upper case, <code>null</code> String
1405 * returns <code>null</code>.</p>
1406 *
1407 * @param str the String to uppercase
1408 * @return the upper cased String
1409 */
1410 public static String upperCase( String str )
1411 {
1412 if ( str == null )
1413 {
1414 return null;
1415 }
1416 return str.toUpperCase();
1417 }
1418
1419 /**
1420 * <p>Convert a String to lower case, <code>null</code> String
1421 * returns <code>null</code>.</p>
1422 *
1423 * @param str the string to lowercase
1424 * @return the lower cased String
1425 */
1426 public static String lowerCase( String str )
1427 {
1428 if ( str == null )
1429 {
1430 return null;
1431 }
1432 return str.toLowerCase();
1433 }
1434
1435 /**
1436 * <p>Uncapitalise a String.</p>
1437 * <p/>
1438 * <p>That is, convert the first character into lower-case.
1439 * <code>null</code> is returned as <code>null</code>.</p>
1440 *
1441 * @param str the String to uncapitalise
1442 * @return uncapitalised String
1443 */
1444 public static String uncapitalise( String str )
1445 {
1446 if ( str == null )
1447 {
1448 return null;
1449 }
1450 else
1451 {
1452 int length = str.length();
1453 if ( length == 0 )
1454 {
1455 return "";
1456 }
1457 else
1458 {
1459 return new StringBuffer( length )
1460 .append( Character.toLowerCase( str.charAt( 0 ) ) )
1461 .append( str, 1, length )
1462 .toString();
1463 }
1464 }
1465 }
1466
1467 /**
1468 * <p>Capitalise a String.</p>
1469 * <p/>
1470 * <p>That is, convert the first character into title-case.
1471 * <code>null</code> is returned as <code>null</code>.</p>
1472 *
1473 * @param str the String to capitalise
1474 * @return capitalised String
1475 */
1476 public static String capitalise( String str )
1477 {
1478 if ( str == null )
1479 {
1480 return null;
1481 }
1482 else
1483 {
1484 int length = str.length();
1485 if ( length == 0 )
1486 {
1487 return "";
1488 }
1489 else
1490 {
1491 return new StringBuilder( length )
1492 .append( Character.toTitleCase( str.charAt( 0 ) ) )
1493 .append( str, 1, length )
1494 .toString();
1495 }
1496 }
1497 }
1498
1499 /**
1500 * <p>Swaps the case of String.</p>
1501 * <p/>
1502 * <p>Properly looks after making sure the start of words
1503 * are Titlecase and not Uppercase.</p>
1504 * <p/>
1505 * <p><code>null</code> is returned as <code>null</code>.</p>
1506 *
1507 * @param str the String to swap the case of
1508 * @return the modified String
1509 */
1510 public static String swapCase( String str )
1511 {
1512 if ( str == null )
1513 {
1514 return null;
1515 }
1516 int sz = str.length();
1517 StringBuilder buffer = new StringBuilder( sz );
1518
1519 boolean whitespace = false;
1520 char ch;
1521 char tmp;
1522
1523 for ( int i = 0; i < sz; i++ )
1524 {
1525 ch = str.charAt( i );
1526 if ( Character.isUpperCase( ch ) )
1527 {
1528 tmp = Character.toLowerCase( ch );
1529 }
1530 else if ( Character.isTitleCase( ch ) )
1531 {
1532 tmp = Character.toLowerCase( ch );
1533 }
1534 else if ( Character.isLowerCase( ch ) )
1535 {
1536 if ( whitespace )
1537 {
1538 tmp = Character.toTitleCase( ch );
1539 }
1540 else
1541 {
1542 tmp = Character.toUpperCase( ch );
1543 }
1544 }
1545 else
1546 {
1547 tmp = ch;
1548 }
1549 buffer.append( tmp );
1550 whitespace = Character.isWhitespace( ch );
1551 }
1552 return buffer.toString();
1553 }
1554
1555
1556 /**
1557 * <p>Capitalise all the words in a String.</p>
1558 * <p/>
1559 * <p>Uses {@link Character#isWhitespace(char)} as a
1560 * separator between words.</p>
1561 * <p/>
1562 * <p><code>null</code> will return <code>null</code>.</p>
1563 *
1564 * @param str the String to capitalise
1565 * @return capitalised String
1566 */
1567 public static String capitaliseAllWords( String str )
1568 {
1569 if ( str == null )
1570 {
1571 return null;
1572 }
1573 int sz = str.length();
1574 StringBuilder buffer = new StringBuilder( sz );
1575 boolean space = true;
1576 for ( int i = 0; i < sz; i++ )
1577 {
1578 char ch = str.charAt( i );
1579 if ( Character.isWhitespace( ch ) )
1580 {
1581 buffer.append( ch );
1582 space = true;
1583 }
1584 else if ( space )
1585 {
1586 buffer.append( Character.toTitleCase( ch ) );
1587 space = false;
1588 }
1589 else
1590 {
1591 buffer.append( ch );
1592 }
1593 }
1594 return buffer.toString();
1595 }
1596
1597 /**
1598 * <p>Uncapitalise all the words in a string.</p>
1599 * <p/>
1600 * <p>Uses {@link Character#isWhitespace(char)} as a
1601 * separator between words.</p>
1602 * <p/>
1603 * <p><code>null</code> will return <code>null</code>.</p>
1604 *
1605 * @param str the string to uncapitalise
1606 * @return uncapitalised string
1607 */
1608 public static String uncapitaliseAllWords( String str )
1609 {
1610 if ( str == null )
1611 {
1612 return null;
1613 }
1614 int sz = str.length();
1615 StringBuilder buffer = new StringBuilder( sz );
1616 boolean space = true;
1617 for ( int i = 0; i < sz; i++ )
1618 {
1619 char ch = str.charAt( i );
1620 if ( Character.isWhitespace( ch ) )
1621 {
1622 buffer.append( ch );
1623 space = true;
1624 }
1625 else if ( space )
1626 {
1627 buffer.append( Character.toLowerCase( ch ) );
1628 space = false;
1629 }
1630 else
1631 {
1632 buffer.append( ch );
1633 }
1634 }
1635 return buffer.toString();
1636 }
1637
1638 // Nested extraction
1639 //--------------------------------------------------------------------------
1640
1641 /**
1642 * <p>Get the String that is nested in between two instances of the
1643 * same String.</p>
1644 * <p/>
1645 * <p>If <code>str</code> is <code>null</code>, will
1646 * return <code>null</code>.</p>
1647 *
1648 * @param str the String containing nested-string
1649 * @param tag the String before and after nested-string
1650 * @return the String that was nested, or <code>null</code>
1651 * @throws NullPointerException if tag is <code>null</code>
1652 */
1653 public static String getNestedString( String str, @Nonnull String tag )
1654 {
1655 return getNestedString( str, tag, tag );
1656 }
1657
1658 /**
1659 * <p>Get the String that is nested in between two Strings.</p>
1660 *
1661 * @param str the String containing nested-string
1662 * @param open the String before nested-string
1663 * @param close the String after nested-string
1664 * @return the String that was nested, or <code>null</code>
1665 * @throws NullPointerException if open or close is <code>null</code>
1666 */
1667 public static String getNestedString( String str, @Nonnull String open, @Nonnull String close )
1668 {
1669 if ( str == null )
1670 {
1671 return null;
1672 }
1673 int start = str.indexOf( open );
1674 if ( start != -1 )
1675 {
1676 int end = str.indexOf( close, start + open.length() );
1677 if ( end != -1 )
1678 {
1679 return str.substring( start + open.length(), end );
1680 }
1681 }
1682 return null;
1683 }
1684
1685 /**
1686 * <p>How many times is the substring in the larger String.</p>
1687 * <p/>
1688 * <p><code>null</code> returns <code>0</code>.</p>
1689 *
1690 * @param str the String to check
1691 * @param sub the substring to count
1692 * @return the number of occurances, 0 if the String is <code>null</code>
1693 * @throws NullPointerException if sub is <code>null</code>
1694 */
1695 public static int countMatches( @Nullable String str, @Nonnull String sub )
1696 {
1697 if ( sub.equals( "" ) )
1698 {
1699 return 0;
1700 }
1701 if ( str == null )
1702 {
1703 return 0;
1704 }
1705 int count = 0;
1706 int idx = 0;
1707 while ( ( idx = str.indexOf( sub, idx ) ) != -1 )
1708 {
1709 count++;
1710 idx += sub.length();
1711 }
1712 return count;
1713 }
1714
1715 // Character Tests
1716 //--------------------------------------------------------------------------
1717
1718 /**
1719 * <p>Checks if the String contains only unicode letters.</p>
1720 * <p/>
1721 * <p><code>null</code> will return <code>false</code>.
1722 * An empty String will return <code>true</code>.</p>
1723 *
1724 * @param str the String to check
1725 * @return <code>true</code> if only contains letters, and is non-null
1726 */
1727 public static boolean isAlpha( String str )
1728 {
1729 if ( str == null )
1730 {
1731 return false;
1732 }
1733 int sz = str.length();
1734 for ( int i = 0; i < sz; i++ )
1735 {
1736 if ( !Character.isLetter( str.charAt( i ) ) )
1737 {
1738 return false;
1739 }
1740 }
1741 return true;
1742 }
1743
1744 /**
1745 * <p>Checks if the String contains only whitespace.</p>
1746 * <p/>
1747 * <p><code>null</code> will return <code>false</code>. An
1748 * empty String will return <code>true</code>.</p>
1749 *
1750 * @param str the String to check
1751 * @return <code>true</code> if only contains whitespace, and is non-null
1752 */
1753 public static boolean isWhitespace( String str )
1754 {
1755 if ( str == null )
1756 {
1757 return false;
1758 }
1759 int sz = str.length();
1760 for ( int i = 0; i < sz; i++ )
1761 {
1762 if ( ( !Character.isWhitespace( str.charAt( i ) ) ) )
1763 {
1764 return false;
1765 }
1766 }
1767 return true;
1768 }
1769
1770 /**
1771 * <p>Checks if the String contains only unicode letters and
1772 * space (<code>' '</code>).</p>
1773 * <p/>
1774 * <p><code>null</code> will return <code>false</code>. An
1775 * empty String will return <code>true</code>.</p>
1776 *
1777 * @param str the String to check
1778 * @return <code>true</code> if only contains letters and space,
1779 * and is non-null
1780 */
1781 public static boolean isAlphaSpace( String str )
1782 {
1783 if ( str == null )
1784 {
1785 return false;
1786 }
1787 int sz = str.length();
1788 for ( int i = 0; i < sz; i++ )
1789 {
1790 if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
1791 {
1792 return false;
1793 }
1794 }
1795 return true;
1796 }
1797
1798 /**
1799 * <p>Checks if the String contains only unicode letters or digits.</p>
1800 * <p/>
1801 * <p><code>null</code> will return <code>false</code>. An empty
1802 * String will return <code>true</code>.</p>
1803 *
1804 * @param str the String to check
1805 * @return <code>true</code> if only contains letters or digits,
1806 * and is non-null
1807 */
1808 public static boolean isAlphanumeric( String str )
1809 {
1810 if ( str == null )
1811 {
1812 return false;
1813 }
1814 int sz = str.length();
1815 for ( int i = 0; i < sz; i++ )
1816 {
1817 if ( !Character.isLetterOrDigit( str.charAt( i ) ) )
1818 {
1819 return false;
1820 }
1821 }
1822 return true;
1823 }
1824
1825 /**
1826 * <p>Checks if the String contains only unicode letters, digits
1827 * or space (<code>' '</code>).</p>
1828 * <p/>
1829 * <p><code>null</code> will return <code>false</code>. An empty
1830 * String will return <code>true</code>.</p>
1831 *
1832 * @param str the String to check
1833 * @return <code>true</code> if only contains letters, digits or space,
1834 * and is non-null
1835 */
1836 public static boolean isAlphanumericSpace( String str )
1837 {
1838 if ( str == null )
1839 {
1840 return false;
1841 }
1842 int sz = str.length();
1843 for ( int i = 0; i < sz; i++ )
1844 {
1845 if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) )
1846 {
1847 return false;
1848 }
1849 }
1850 return true;
1851 }
1852
1853 /**
1854 * <p>Checks if the String contains only unicode digits.</p>
1855 * <p/>
1856 * <p><code>null</code> will return <code>false</code>.
1857 * An empty String will return <code>true</code>.</p>
1858 *
1859 * @param str the String to check
1860 * @return <code>true</code> if only contains digits, and is non-null
1861 */
1862 public static boolean isNumeric( String str )
1863 {
1864 if ( str == null )
1865 {
1866 return false;
1867 }
1868 int sz = str.length();
1869 for ( int i = 0; i < sz; i++ )
1870 {
1871 if ( !Character.isDigit( str.charAt( i ) ) )
1872 {
1873 return false;
1874 }
1875 }
1876 return true;
1877 }
1878
1879 // Defaults
1880 //--------------------------------------------------------------------------
1881
1882 /**
1883 * <p>Returns either the passed in <code>Object</code> as a String,
1884 * or, if the <code>Object</code> is <code>null</code>, an empty
1885 * String.</p>
1886 *
1887 * @param obj the Object to check
1888 * @return the passed in Object's toString, or blank if it was
1889 * <code>null</code>
1890 */
1891 @Nonnull public static String defaultString( Object obj )
1892 {
1893 return defaultString( obj, "" );
1894 }
1895
1896 /**
1897 * <p>Returns either the passed in <code>Object</code> as a String,
1898 * or, if the <code>Object</code> is <code>null</code>, a passed
1899 * in default String.</p>
1900 *
1901 * @param obj the Object to check
1902 * @param defaultString the default String to return if str is
1903 * <code>null</code>
1904 * @return the passed in string, or the default if it was
1905 * <code>null</code>
1906 */
1907 @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString )
1908 {
1909 return ( obj == null ) ? defaultString : obj.toString();
1910 }
1911
1912 // Reversing
1913 //--------------------------------------------------------------------------
1914
1915 /**
1916 * <p>Reverse a String.</p>
1917 * <p/>
1918 * <p><code>null</code> String returns <code>null</code>.</p>
1919 *
1920 * @param str the String to reverse
1921 * @return the reversed String
1922 */
1923 public static String reverse( String str )
1924 {
1925 if ( str == null )
1926 {
1927 return null;
1928 }
1929 return new StringBuffer( str ).reverse().toString();
1930 }
1931
1932 /**
1933 * <p>Reverses a String that is delimited by a specific character.</p>
1934 * <p/>
1935 * <p>The Strings between the delimiters are not reversed.
1936 * Thus java.lang.String becomes String.lang.java (if the delimiter
1937 * is <code>'.'</code>).</p>
1938 *
1939 * @param str the String to reverse
1940 * @param delimiter the delimiter to use
1941 * @return the reversed String
1942 */
1943 @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter )
1944 {
1945 // could implement manually, but simple way is to reuse other,
1946 // probably slower, methods.
1947 String[] strs = split( str, delimiter );
1948 reverseArray( strs );
1949 return join( strs, delimiter );
1950 }
1951
1952 /**
1953 * <p>Reverses an array.</p>
1954 * <p/>
1955 * <p>TAKEN FROM CollectionsUtils.</p>
1956 *
1957 * @param array the array to reverse
1958 */
1959 private static void reverseArray( @Nonnull String... array )
1960 {
1961 int i = 0;
1962 int j = array.length - 1;
1963 String tmp;
1964
1965 while ( j > i )
1966 {
1967 tmp = array[j];
1968 array[j] = array[i];
1969 array[i] = tmp;
1970 j--;
1971 i++;
1972 }
1973 }
1974
1975 // Abbreviating
1976 //--------------------------------------------------------------------------
1977
1978 /**
1979 * Turn "Now is the time for all good men" into "Now is the time for..."
1980 * <p/>
1981 * Specifically:
1982 * <p/>
1983 * If str is less than max characters long, return it.
1984 * Else abbreviate it to (substring(str, 0, max-3) + "...").
1985 * If maxWidth is less than 3, throw an IllegalArgumentException.
1986 * In no case will it return a string of length greater than maxWidth.
1987 *
1988 * @param s The string to be abbreviated.
1989 * @param maxWidth maximum length of result string
1990 * @return The abbreviated string.
1991 */
1992 @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth )
1993 {
1994 return abbreviate( s, 0, maxWidth );
1995 }
1996
1997 /**
1998 * Turn "Now is the time for all good men" into "...is the time for..."
1999 * <p/>
2000 * Works like abbreviate(String, int), but allows you to specify a "left edge"
2001 * offset. Note that this left edge is not necessarily going to be the leftmost
2002 * character in the result, or the first
2003 * character following the ellipses, but it will appear somewhere in the result.
2004 * In no case will it return a string of length greater than maxWidth.
2005 *
2006 * @param s String to abbreviate.
2007 * @param offset left edge of source string
2008 * @param maxWidth maximum length of result string
2009 * @return The abbreviated string.
2010 */
2011 @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth )
2012 {
2013 if ( maxWidth < 4 )
2014 {
2015 throw new IllegalArgumentException( "Minimum abbreviation width is 4" );
2016 }
2017 if ( s.length() <= maxWidth )
2018 {
2019 return s;
2020 }
2021 if ( offset > s.length() )
2022 {
2023 offset = s.length();
2024 }
2025 if ( ( s.length() - offset ) < ( maxWidth - 3 ) )
2026 {
2027 offset = s.length() - ( maxWidth - 3 );
2028 }
2029 if ( offset <= 4 )
2030 {
2031 return s.substring( 0, maxWidth - 3 ) + "...";
2032 }
2033 if ( maxWidth < 7 )
2034 {
2035 throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" );
2036 }
2037 if ( ( offset + ( maxWidth - 3 ) ) < s.length() )
2038 {
2039 return "..." + abbreviate( s.substring( offset ), maxWidth - 3 );
2040 }
2041 return "..." + s.substring( s.length() - ( maxWidth - 3 ) );
2042 }
2043
2044 // Difference
2045 //--------------------------------------------------------------------------
2046
2047 /**
2048 * Compare two strings, and return the portion where they differ.
2049 * (More precisely, return the remainder of the second string,
2050 * starting from where it's different from the first.)
2051 * <p/>
2052 * E.g. strdiff("i am a machine", "i am a robot") -> "robot"
2053 *
2054 * @param s1 The first string.
2055 * @param s2 The second string.
2056 * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal
2057 */
2058 public static String difference( @Nonnull String s1, @Nonnull String s2 )
2059 {
2060 int at = differenceAt( s1, s2 );
2061 if ( at == -1 )
2062 {
2063 return "";
2064 }
2065 return s2.substring( at );
2066 }
2067
2068 /**
2069 * Compare two strings, and return the index at which the strings begin to differ.
2070 * <p>
2071 * E.g. strdiff("i am a machine", "i am a robot") -> 7
2072 * </p>
2073 *
2074 * @param s1 The first string.
2075 * @param s2 The second string.
2076 * @return the index where s2 and s1 begin to differ; -1 if they are equal
2077 */
2078 public static int differenceAt( @Nonnull String s1, @Nonnull String s2 )
2079 {
2080 int i;
2081 for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i )
2082 {
2083 if ( s1.charAt( i ) != s2.charAt( i ) )
2084 {
2085 break;
2086 }
2087 }
2088 if ( ( i < s2.length() ) || ( i < s1.length() ) )
2089 {
2090 return i;
2091 }
2092 return -1;
2093 }
2094
2095 /**
2096 * Fill all 'variables' in the given text with the values from the map.
2097 * Any text looking like '${key}' will get replaced by the value stored
2098 * in the namespace map under the 'key'.
2099 *
2100 * @param text The text where replacements will be searched for.
2101 * @param namespace The namespace which contains the replacements.
2102 * @return the interpolated text.
2103 */
2104 public static String interpolate( String text, @Nonnull Map<?, ?> namespace )
2105 {
2106 for ( Map.Entry<?, ?> entry : namespace.entrySet() )
2107 {
2108 String key = entry.getKey().toString();
2109
2110 Object obj = entry.getValue();
2111
2112 if ( obj == null )
2113 {
2114 throw new NullPointerException( "The value of the key '" + key + "' is null." );
2115 }
2116
2117 String value = obj.toString();
2118
2119 text = replace( text, "${" + key + "}", value );
2120
2121 if ( !key.contains( " " ) )
2122 {
2123 text = replace( text, "$" + key, value );
2124 }
2125 }
2126 return text;
2127 }
2128
2129 /**
2130 * This is basically the inverse of {@link #addAndDeHump(String)}.
2131 * It will remove the 'replaceThis' parameter and uppercase the next
2132 * character afterwards.
2133 * <pre>
2134 * removeAndHump( "this-is-it", %quot;-" );
2135 * </pre>
2136 * will become 'ThisIsIt'.
2137 *
2138 * @param data The data.
2139 * @param replaceThis The things which should be replaced.
2140 * @return humped String
2141 */
2142 @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis )
2143 {
2144 String temp;
2145
2146 StringBuilder out = new StringBuilder();
2147
2148 temp = data;
2149
2150 StringTokenizer st = new StringTokenizer( temp, replaceThis );
2151
2152 while ( st.hasMoreTokens() )
2153 {
2154 String element = st.nextToken();
2155
2156 out.append( capitalizeFirstLetter( element ) );
2157 }
2158
2159 return out.toString();
2160 }
2161
2162 /**
2163 * Convert the first character of the given String to uppercase.
2164 * This method will <i>not</i> trim of spaces!
2165 * <p/>
2166 * <p>
2167 * <b>Attention:</b> this method will currently throw a
2168 * <code>IndexOutOfBoundsException</code> for empty strings!
2169 * </p>
2170 *
2171 * @param data the String to get capitalized
2172 * @return data string with the first character transformed to uppercase
2173 * @throws NullPointerException if data is <code>null</code>
2174 */
2175 @Nonnull public static String capitalizeFirstLetter( @Nonnull String data )
2176 {
2177 char firstChar = data.charAt( 0 );
2178 char titleCase = Character.toTitleCase( firstChar );
2179 if ( firstChar == titleCase )
2180 {
2181 return data;
2182 }
2183 StringBuilder result = new StringBuilder( data.length() );
2184 result.append( titleCase );
2185 result.append( data, 1, data.length() );
2186 return result.toString();
2187 }
2188
2189 /**
2190 * Convert the first character of the given String to lowercase.
2191 * This method will <i>not</i> trim of spaces!
2192 * <p/>
2193 * <p>
2194 * <b>Attention:</b> this method will currently throw a
2195 * <code>IndexOutOfBoundsException</code> for empty strings!
2196 * </p>
2197 *
2198 * @param data the String to get it's first character lower-cased.
2199 * @return data string with the first character transformed to lowercase
2200 * @throws NullPointerException if data is <code>null</code>
2201 */
2202 @Nonnull public static String lowercaseFirstLetter( @Nonnull String data )
2203 {
2204 char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) );
2205
2206 String restLetters = data.substring( 1 );
2207
2208 return firstLetter + restLetters;
2209 }
2210
2211 /**
2212 * Take the input string and un-camel-case it.
2213 * <p/>
2214 * 'ThisIsIt' will become 'this-is-it'.
2215 *
2216 * @param view The view.
2217 * @return deHumped String
2218 */
2219 @Nonnull public static String addAndDeHump( @Nonnull String view )
2220 {
2221 StringBuilder sb = new StringBuilder();
2222
2223 for ( int i = 0; i < view.length(); i++ )
2224 {
2225 if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) )
2226 {
2227 sb.append( '-' );
2228 }
2229
2230 sb.append( view.charAt( i ) );
2231 }
2232
2233 return sb.toString().trim().toLowerCase( Locale.ENGLISH );
2234 }
2235
2236 /**
2237 * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
2238 * <p/>
2239 * <pre>
2240 * StringUtils.quoteAndEscape(null, *) = null
2241 * StringUtils.quoteAndEscape("", *) = ""
2242 * StringUtils.quoteAndEscape("abc", '"') = abc
2243 * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
2244 * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
2245 * </pre>
2246 *
2247 * @param source The source.
2248 * @param quoteChar The quote character.
2249 * @return the String quoted and escaped
2250 * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2251 * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2252 *
2253 */
2254 public static String quoteAndEscape( @Nullable String source, char quoteChar )
2255 {
2256 return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false );
2257 }
2258
2259 /**
2260 * <p>Quote and escape a String with the given character, handling <code>null</code>.</p>
2261 *
2262 * @param source The source.
2263 * @param quoteChar The quote character.
2264 * @param quotingTriggers The quoting trigger.
2265 * @return the String quoted and escaped
2266 * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2267 *
2268 */
2269 public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers )
2270 {
2271 return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false );
2272 }
2273
2274 /**
2275 * @param source The source.
2276 * @param quoteChar The quote character.
2277 * @param escapedChars The escaped characters.
2278 * @param escapeChar The escape character.
2279 * @param force true/false.
2280 * @return the String quoted and escaped
2281 * @see #quoteAndEscape(String, char, char[], char[], char, boolean)
2282 *
2283 */
2284 public static String quoteAndEscape( @Nullable String source, char quoteChar,
2285 @Nonnull final char[] escapedChars, char escapeChar, boolean force )
2286 {
2287 return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force );
2288 }
2289
2290 /**
2291 * @param source The source.
2292 * @param quoteChar The quote character.
2293 * @param escapedChars The escaped characters.
2294 * @param quotingTriggers The quoting trigger.
2295 * @param escapeChar The escape character.
2296 * @param force true/false.
2297 * @return the String quoted and escaped
2298 */
2299 public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars,
2300 @Nonnull final char[] quotingTriggers, char escapeChar, boolean force )
2301 {
2302 if ( source == null )
2303 {
2304 return null;
2305 }
2306
2307 if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith(
2308 Character.toString( quoteChar ) ) )
2309 {
2310 return source;
2311 }
2312
2313 String escaped = escape( source, escapedChars, escapeChar );
2314
2315 boolean quote = false;
2316 if ( force )
2317 {
2318 quote = true;
2319 }
2320 else if ( !escaped.equals( source ) )
2321 {
2322 quote = true;
2323 }
2324 else
2325 {
2326 for ( char quotingTrigger : quotingTriggers )
2327 {
2328 if ( escaped.indexOf( quotingTrigger ) > -1 )
2329 {
2330 quote = true;
2331 break;
2332 }
2333 }
2334 }
2335
2336 if ( quote )
2337 {
2338 return quoteChar + escaped + quoteChar;
2339 }
2340
2341 return escaped;
2342 }
2343
2344 /**
2345 * @param source The source.
2346 * @param escapedChars escape characters.
2347 * @param escapeChar escape character.
2348 * @return the String escaped
2349 */
2350 public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar )
2351 {
2352 if ( source == null )
2353 {
2354 return null;
2355 }
2356
2357 char[] eqc = new char[escapedChars.length];
2358 System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length );
2359 Arrays.sort( eqc );
2360
2361 StringBuilder buffer = new StringBuilder( source.length() );
2362
2363 for ( int i = 0; i < source.length(); i++ )
2364 {
2365 final char c = source.charAt( i );
2366 int result = Arrays.binarySearch( eqc, c );
2367
2368 if ( result > -1 )
2369 {
2370 buffer.append( escapeChar );
2371 }
2372 buffer.append( c );
2373 }
2374
2375 return buffer.toString();
2376 }
2377
2378 /**
2379 * Remove all duplicate whitespace characters and line terminators are replaced with a single
2380 * space.
2381 *
2382 * @param s a not null String
2383 * @return a string with unique whitespace.
2384 *
2385 */
2386 @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s )
2387 {
2388 StringBuilder result = new StringBuilder();
2389 int length = s.length();
2390 boolean isPreviousWhiteSpace = false;
2391 for ( int i = 0; i < length; i++ )
2392 {
2393 char c = s.charAt( i );
2394 boolean thisCharWhiteSpace = Character.isWhitespace( c );
2395 if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) )
2396 {
2397 result.append( c );
2398 }
2399 isPreviousWhiteSpace = thisCharWhiteSpace;
2400 }
2401 return result.toString();
2402 }
2403
2404 /**
2405 * Parses the given String and replaces all occurrences of
2406 * '\n', '\r' and '\r\n' with the system line separator.
2407 *
2408 * @param s a not null String
2409 * @return a String that contains only System line separators.
2410 * @see #unifyLineSeparators(String, String)
2411 *
2412 */
2413 public static String unifyLineSeparators( @Nullable String s )
2414 {
2415 return unifyLineSeparators( s, System.getProperty( "line.separator" ) );
2416 }
2417
2418 /**
2419 * Parses the given String and replaces all occurrences of
2420 * '\n', '\r' and '\r\n' with the system line separator.
2421 *
2422 * @param s a not null String
2423 * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator.
2424 * @return a String that contains only System line separators.
2425 * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters.
2426 *
2427 */
2428 public static String unifyLineSeparators( @Nullable String s, @Nullable String ls )
2429 {
2430 if ( s == null )
2431 {
2432 return null;
2433 }
2434
2435 if ( ls == null )
2436 {
2437 ls = System.getProperty( "line.separator" );
2438 }
2439
2440 if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) )
2441 {
2442 throw new IllegalArgumentException( "Requested line separator is invalid." );
2443 }
2444
2445 int length = s.length();
2446
2447 StringBuilder buffer = new StringBuilder( length );
2448 for ( int i = 0; i < length; i++ )
2449 {
2450 if ( s.charAt( i ) == '\r' )
2451 {
2452 if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' )
2453 {
2454 i++;
2455 }
2456
2457 buffer.append( ls );
2458 }
2459 else if ( s.charAt( i ) == '\n' )
2460 {
2461 buffer.append( ls );
2462 }
2463 else
2464 {
2465 buffer.append( s.charAt( i ) );
2466 }
2467 }
2468
2469 return buffer.toString();
2470 }
2471
2472 /**
2473 * <p>Checks if String contains a search character, handling <code>null</code>.
2474 * This method uses {@link String#indexOf(int)}.</p>
2475 * <p/>
2476 * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p>
2477 * <p/>
2478 * <pre>
2479 * StringUtils.contains(null, *) = false
2480 * StringUtils.contains("", *) = false
2481 * StringUtils.contains("abc", 'a') = true
2482 * StringUtils.contains("abc", 'z') = false
2483 * </pre>
2484 *
2485 * @param str the String to check, may be null
2486 * @param searchChar the character to find
2487 * @return true if the String contains the search character,
2488 * false if not or <code>null</code> string input
2489 *
2490 */
2491 public static boolean contains( @Nullable String str, char searchChar )
2492 {
2493 return !isEmpty( str ) && str.indexOf( searchChar ) >= 0;
2494 }
2495
2496 /**
2497 * <p>Checks if String contains a search String, handling <code>null</code>.
2498 * This method uses {@link String#indexOf(int)}.</p>
2499 * <p/>
2500 * <p>A <code>null</code> String will return <code>false</code>.</p>
2501 * <p/>
2502 * <pre>
2503 * StringUtils.contains(null, *) = false
2504 * StringUtils.contains(*, null) = false
2505 * StringUtils.contains("", "") = true
2506 * StringUtils.contains("abc", "") = true
2507 * StringUtils.contains("abc", "a") = true
2508 * StringUtils.contains("abc", "z") = false
2509 * </pre>
2510 *
2511 * @param str the String to check, may be null
2512 * @param searchStr the String to find, may be null
2513 * @return true if the String contains the search String,
2514 * false if not or <code>null</code> string input
2515 */
2516 public static boolean contains( @Nullable String str, @Nullable String searchStr )
2517 {
2518 return !( str == null || searchStr == null ) && str.contains( searchStr );
2519 }
2520
2521 /**
2522 * <p>Checks if String ends with a search String, handling <code>null</code>.</p>
2523 * <p/>
2524 * <p>A <code>null</code> String will return <code>false</code>.</p>
2525 * <p/>
2526 * <pre>
2527 * StringUtils.endsWithIgnoreCase(null, *) = false
2528 * StringUtils.endsWithIgnoreCase(*, null) = false
2529 * StringUtils.endsWithIgnoreCase("", "") = true
2530 * StringUtils.endsWithIgnoreCase("abc", "") = true
2531 * StringUtils.endsWithIgnoreCase("abc", "C") = true
2532 * StringUtils.endsWithIgnoreCase("abc", "a") = false
2533 * </pre>
2534 *
2535 * @param str the String to check, may be null
2536 * @param searchStr the String to find at end, may be null
2537 * @return true if the String ends with the search String,
2538 * false if not or <code>null</code> string input
2539 *
2540 */
2541 public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr )
2542 {
2543 if ( str == null || searchStr == null )
2544 {
2545 // for consistency with contains
2546 return false;
2547 }
2548
2549 if ( str.length() < searchStr.length() )
2550 {
2551 return false;
2552 }
2553
2554 return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() );
2555 }
2556 }