Coverage Report - org.apache.commons.lang3.text.WordUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
WordUtils
100%
133/133
98%
95/96
5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.lang3.text;
 18  
 
 19  
 import java.util.regex.Matcher;
 20  
 import java.util.regex.Pattern;
 21  
 
 22  
 import org.apache.commons.lang3.ArrayUtils;
 23  
 import org.apache.commons.lang3.StringUtils;
 24  
 
 25  
 /**
 26  
  * <p>Operations on Strings that contain words.</p>
 27  
  *
 28  
  * <p>This class tries to handle <code>null</code> input gracefully.
 29  
  * An exception will not be thrown for a <code>null</code> input.
 30  
  * Each method documents its behaviour in more detail.</p>
 31  
  *
 32  
  * @since 2.0
 33  
  * @deprecated as of 3.6, use commons-text
 34  
  * <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/WordUtils.html">
 35  
  * WordUtils</a> instead
 36  
  */
 37  
 @Deprecated
 38  
 public class WordUtils {
 39  
 
 40  
     /**
 41  
      * <p><code>WordUtils</code> instances should NOT be constructed in
 42  
      * standard programming. Instead, the class should be used as
 43  
      * <code>WordUtils.wrap("foo bar", 20);</code>.</p>
 44  
      *
 45  
      * <p>This constructor is public to permit tools that require a JavaBean
 46  
      * instance to operate.</p>
 47  
      */
 48  
     public WordUtils() {
 49  1
       super();
 50  1
     }
 51  
 
 52  
     // Wrapping
 53  
     //--------------------------------------------------------------------------
 54  
     /**
 55  
      * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
 56  
      *
 57  
      * <p>New lines will be separated by the system property line separator.
 58  
      * Very long words, such as URLs will <i>not</i> be wrapped.</p>
 59  
      *
 60  
      * <p>Leading spaces on a new line are stripped.
 61  
      * Trailing spaces are not stripped.</p>
 62  
      *
 63  
      * <table border="1" summary="Wrap Results">
 64  
      *  <tr>
 65  
      *   <th>input</th>
 66  
      *   <th>wrapLength</th>
 67  
      *   <th>result</th>
 68  
      *  </tr>
 69  
      *  <tr>
 70  
      *   <td>null</td>
 71  
      *   <td>*</td>
 72  
      *   <td>null</td>
 73  
      *  </tr>
 74  
      *  <tr>
 75  
      *   <td>""</td>
 76  
      *   <td>*</td>
 77  
      *   <td>""</td>
 78  
      *  </tr>
 79  
      *  <tr>
 80  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 81  
      *   <td>20</td>
 82  
      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
 83  
      *  </tr>
 84  
      *  <tr>
 85  
      *   <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
 86  
      *   <td>20</td>
 87  
      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
 88  
      *  </tr>
 89  
      *  <tr>
 90  
      *   <td>"Click here, http://commons.apache.org, to jump to the commons website"</td>
 91  
      *   <td>20</td>
 92  
      *   <td>"Click here,\nhttp://commons.apache.org,\nto jump to the\ncommons website"</td>
 93  
      *  </tr>
 94  
      * </table>
 95  
      *
 96  
      * (assuming that '\n' is the systems line separator)
 97  
      *
 98  
      * @param str  the String to be word wrapped, may be null
 99  
      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
 100  
      * @return a line with newlines inserted, <code>null</code> if null input
 101  
      */
 102  
     public static String wrap(final String str, final int wrapLength) {
 103  9
         return wrap(str, wrapLength, null, false);
 104  
     }
 105  
 
 106  
     /**
 107  
      * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
 108  
      *
 109  
      * <p>Leading spaces on a new line are stripped.
 110  
      * Trailing spaces are not stripped.</p>
 111  
      *
 112  
      * <table border="1" summary="Wrap Results">
 113  
      *  <tr>
 114  
      *   <th>input</th>
 115  
      *   <th>wrapLength</th>
 116  
      *   <th>newLineString</th>
 117  
      *   <th>wrapLongWords</th>
 118  
      *   <th>result</th>
 119  
      *  </tr>
 120  
      *  <tr>
 121  
      *   <td>null</td>
 122  
      *   <td>*</td>
 123  
      *   <td>*</td>
 124  
      *   <td>true/false</td>
 125  
      *   <td>null</td>
 126  
      *  </tr>
 127  
      *  <tr>
 128  
      *   <td>""</td>
 129  
      *   <td>*</td>
 130  
      *   <td>*</td>
 131  
      *   <td>true/false</td>
 132  
      *   <td>""</td>
 133  
      *  </tr>
 134  
      *  <tr>
 135  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 136  
      *   <td>20</td>
 137  
      *   <td>"\n"</td>
 138  
      *   <td>true/false</td>
 139  
      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
 140  
      *  </tr>
 141  
      *  <tr>
 142  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 143  
      *   <td>20</td>
 144  
      *   <td>"&lt;br /&gt;"</td>
 145  
      *   <td>true/false</td>
 146  
      *   <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
 147  
      *  </tr>
 148  
      *  <tr>
 149  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 150  
      *   <td>20</td>
 151  
      *   <td>null</td>
 152  
      *   <td>true/false</td>
 153  
      *   <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
 154  
      *  </tr>
 155  
      *  <tr>
 156  
      *   <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
 157  
      *   <td>20</td>
 158  
      *   <td>"\n"</td>
 159  
      *   <td>false</td>
 160  
      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
 161  
      *  </tr>
 162  
      *  <tr>
 163  
      *   <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
 164  
      *   <td>20</td>
 165  
      *   <td>"\n"</td>
 166  
      *   <td>true</td>
 167  
      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td>
 168  
      *  </tr>
 169  
      * </table>
 170  
      *
 171  
      * @param str  the String to be word wrapped, may be null
 172  
      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
 173  
      * @param newLineStr  the string to insert for a new line,
 174  
      *  <code>null</code> uses the system property line separator
 175  
      * @param wrapLongWords  true if long words (such as URLs) should be wrapped
 176  
      * @return a line with newlines inserted, <code>null</code> if null input
 177  
      */
 178  
     public static String wrap(final String str, final int wrapLength, final String newLineStr, final boolean wrapLongWords) {
 179  40
         return wrap(str, wrapLength, newLineStr, wrapLongWords, " ");
 180  
     }
 181  
 
 182  
     /**
 183  
      * <p>Wraps a single line of text, identifying words by <code>wrapOn</code>.</p>
 184  
      *
 185  
      * <p>Leading spaces on a new line are stripped.
 186  
      * Trailing spaces are not stripped.</p>
 187  
      *
 188  
      * <table border="1" summary="Wrap Results">
 189  
      *  <tr>
 190  
      *   <th>input</th>
 191  
      *   <th>wrapLength</th>
 192  
      *   <th>newLineString</th>
 193  
      *   <th>wrapLongWords</th>
 194  
      *   <th>wrapOn</th>
 195  
      *   <th>result</th>
 196  
      *  </tr>
 197  
      *  <tr>
 198  
      *   <td>null</td>
 199  
      *   <td>*</td>
 200  
      *   <td>*</td>
 201  
      *   <td>true/false</td>
 202  
      *   <td>*</td>
 203  
      *   <td>null</td>
 204  
      *  </tr>
 205  
      *  <tr>
 206  
      *   <td>""</td>
 207  
      *   <td>*</td>
 208  
      *   <td>*</td>
 209  
      *   <td>true/false</td>
 210  
      *   <td>*</td>
 211  
      *   <td>""</td>
 212  
      *  </tr>
 213  
      *  <tr>
 214  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 215  
      *   <td>20</td>
 216  
      *   <td>"\n"</td>
 217  
      *   <td>true/false</td>
 218  
      *   <td>" "</td>
 219  
      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
 220  
      *  </tr>
 221  
      *  <tr>
 222  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 223  
      *   <td>20</td>
 224  
      *   <td>"&lt;br /&gt;"</td>
 225  
      *   <td>true/false</td>
 226  
      *   <td>" "</td>
 227  
      *   <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
 228  
      *  </tr>
 229  
      *  <tr>
 230  
      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
 231  
      *   <td>20</td>
 232  
      *   <td>null</td>
 233  
      *   <td>true/false</td>
 234  
      *   <td>" "</td>
 235  
      *   <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
 236  
      *  </tr>
 237  
      *  <tr>
 238  
      *   <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
 239  
      *   <td>20</td>
 240  
      *   <td>"\n"</td>
 241  
      *   <td>false</td>
 242  
      *   <td>" "</td>
 243  
      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td>
 244  
      *  </tr>
 245  
      *  <tr>
 246  
      *   <td>"Click here to jump to the commons website - http://commons.apache.org"</td>
 247  
      *   <td>20</td>
 248  
      *   <td>"\n"</td>
 249  
      *   <td>true</td>
 250  
      *   <td>" "</td>
 251  
      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td>
 252  
      *  </tr>
 253  
      *  <tr>
 254  
      *   <td>"flammable/inflammable"</td>
 255  
      *   <td>20</td>
 256  
      *   <td>"\n"</td>
 257  
      *   <td>true</td>
 258  
      *   <td>"/"</td>
 259  
      *   <td>"flammable\ninflammable"</td>
 260  
      *  </tr>
 261  
      * </table>
 262  
      * @param str  the String to be word wrapped, may be null
 263  
      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
 264  
      * @param newLineStr  the string to insert for a new line,
 265  
      *  <code>null</code> uses the system property line separator
 266  
      * @param wrapLongWords  true if long words (such as URLs) should be wrapped
 267  
      * @param wrapOn regex expression to be used as a breakable characters,
 268  
      *               if blank string is provided a space character will be used
 269  
      * @return a line with newlines inserted, <code>null</code> if null input
 270  
      */
 271  
     public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) {
 272  45
         if (str == null) {
 273  8
             return null;
 274  
         }
 275  37
         if (newLineStr == null) {
 276  13
             newLineStr = System.lineSeparator();
 277  
         }
 278  37
         if (wrapLength < 1) {
 279  4
             wrapLength = 1;
 280  
         }
 281  37
         if (StringUtils.isBlank(wrapOn)) {
 282  32
             wrapOn = " ";
 283  
         }
 284  37
         final Pattern patternToWrapOn = Pattern.compile(wrapOn);
 285  37
         final int inputLineLength = str.length();
 286  37
         int offset = 0;
 287  37
         final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32);
 288  
 
 289  154
         while (offset < inputLineLength) {
 290  141
             int spaceToWrapAt = -1;
 291  141
             Matcher matcher = patternToWrapOn.matcher(str.substring(offset, Math.min(offset + wrapLength + 1, inputLineLength)));
 292  141
             if (matcher.find()) {
 293  115
                 if (matcher.start() == 0) {
 294  35
                     offset += matcher.end();
 295  35
                     continue;
 296  
                 }
 297  80
                 spaceToWrapAt = matcher.start() + offset;
 298  
             }
 299  
 
 300  
             // only last line without leading spaces is left
 301  106
             if(inputLineLength - offset <= wrapLength) {
 302  24
                 break;
 303  
             }
 304  
 
 305  267
             while(matcher.find()){
 306  185
                 spaceToWrapAt = matcher.start() + offset;
 307  
             }
 308  
 
 309  82
             if (spaceToWrapAt >= offset) {
 310  
                 // normal case
 311  65
                 wrappedLine.append(str.substring(offset, spaceToWrapAt));
 312  65
                 wrappedLine.append(newLineStr);
 313  65
                 offset = spaceToWrapAt + 1;
 314  
 
 315  
             } else {
 316  
                 // really long word or URL
 317  17
                 if (wrapLongWords) {
 318  
                     // wrap really long word one line at a time
 319  4
                     wrappedLine.append(str.substring(offset, wrapLength + offset));
 320  4
                     wrappedLine.append(newLineStr);
 321  4
                     offset += wrapLength;
 322  
                 } else {
 323  
                     // do not wrap really long word, just extend beyond limit
 324  13
                     matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength));
 325  13
                     if (matcher.find()) {
 326  8
                         spaceToWrapAt = matcher.start() + offset + wrapLength;
 327  
                     }
 328  
 
 329  13
                     if (spaceToWrapAt >= 0) {
 330  8
                         wrappedLine.append(str.substring(offset, spaceToWrapAt));
 331  8
                         wrappedLine.append(newLineStr);
 332  8
                         offset = spaceToWrapAt + 1;
 333  
                     } else {
 334  5
                         wrappedLine.append(str.substring(offset));
 335  5
                         offset = inputLineLength;
 336  
                     }
 337  
                 }
 338  
             }
 339  82
         }
 340  
 
 341  
         // Whatever is left in line is short enough to just pass through
 342  37
         wrappedLine.append(str.substring(offset));
 343  
 
 344  37
         return wrappedLine.toString();
 345  
     }
 346  
 
 347  
     // Capitalizing
 348  
     //-----------------------------------------------------------------------
 349  
     /**
 350  
      * <p>Capitalizes all the whitespace separated words in a String.
 351  
      * Only the first character of each word is changed. To convert the
 352  
      * rest of each word to lowercase at the same time,
 353  
      * use {@link #capitalizeFully(String)}.</p>
 354  
      *
 355  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 356  
      * A <code>null</code> input String returns <code>null</code>.
 357  
      * Capitalization uses the Unicode title case, normally equivalent to
 358  
      * upper case.</p>
 359  
      *
 360  
      * <pre>
 361  
      * WordUtils.capitalize(null)        = null
 362  
      * WordUtils.capitalize("")          = ""
 363  
      * WordUtils.capitalize("i am FINE") = "I Am FINE"
 364  
      * </pre>
 365  
      *
 366  
      * @param str  the String to capitalize, may be null
 367  
      * @return capitalized String, <code>null</code> if null String input
 368  
      * @see #uncapitalize(String)
 369  
      * @see #capitalizeFully(String)
 370  
      */
 371  
     public static String capitalize(final String str) {
 372  9
         return capitalize(str, null);
 373  
     }
 374  
 
 375  
     /**
 376  
      * <p>Capitalizes all the delimiter separated words in a String.
 377  
      * Only the first character of each word is changed. To convert the
 378  
      * rest of each word to lowercase at the same time,
 379  
      * use {@link #capitalizeFully(String, char[])}.</p>
 380  
      *
 381  
      * <p>The delimiters represent a set of characters understood to separate words.
 382  
      * The first string character and the first non-delimiter character after a
 383  
      * delimiter will be capitalized. </p>
 384  
      *
 385  
      * <p>A <code>null</code> input String returns <code>null</code>.
 386  
      * Capitalization uses the Unicode title case, normally equivalent to
 387  
      * upper case.</p>
 388  
      *
 389  
      * <pre>
 390  
      * WordUtils.capitalize(null, *)            = null
 391  
      * WordUtils.capitalize("", *)              = ""
 392  
      * WordUtils.capitalize(*, new char[0])     = *
 393  
      * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
 394  
      * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
 395  
      * </pre>
 396  
      *
 397  
      * @param str  the String to capitalize, may be null
 398  
      * @param delimiters  set of characters to determine capitalization, null means whitespace
 399  
      * @return capitalized String, <code>null</code> if null String input
 400  
      * @see #uncapitalize(String)
 401  
      * @see #capitalizeFully(String)
 402  
      * @since 2.1
 403  
      */
 404  
     public static String capitalize(final String str, final char... delimiters) {
 405  35
         final int delimLen = delimiters == null ? -1 : delimiters.length;
 406  35
         if (StringUtils.isEmpty(str) || delimLen == 0) {
 407  5
             return str;
 408  
         }
 409  30
         final char[] buffer = str.toCharArray();
 410  30
         boolean capitalizeNext = true;
 411  286
         for (int i = 0; i < buffer.length; i++) {
 412  256
             final char ch = buffer[i];
 413  256
             if (isDelimiter(ch, delimiters)) {
 414  56
                 capitalizeNext = true;
 415  200
             } else if (capitalizeNext) {
 416  80
                 buffer[i] = Character.toTitleCase(ch);
 417  80
                 capitalizeNext = false;
 418  
             }
 419  
         }
 420  30
         return new String(buffer);
 421  
     }
 422  
 
 423  
     //-----------------------------------------------------------------------
 424  
     /**
 425  
      * <p>Converts all the whitespace separated words in a String into capitalized words,
 426  
      * that is each word is made up of a titlecase character and then a series of
 427  
      * lowercase characters.  </p>
 428  
      *
 429  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 430  
      * A <code>null</code> input String returns <code>null</code>.
 431  
      * Capitalization uses the Unicode title case, normally equivalent to
 432  
      * upper case.</p>
 433  
      *
 434  
      * <pre>
 435  
      * WordUtils.capitalizeFully(null)        = null
 436  
      * WordUtils.capitalizeFully("")          = ""
 437  
      * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
 438  
      * </pre>
 439  
      *
 440  
      * @param str  the String to capitalize, may be null
 441  
      * @return capitalized String, <code>null</code> if null String input
 442  
      */
 443  
     public static String capitalizeFully(final String str) {
 444  9
         return capitalizeFully(str, null);
 445  
     }
 446  
 
 447  
     /**
 448  
      * <p>Converts all the delimiter separated words in a String into capitalized words,
 449  
      * that is each word is made up of a titlecase character and then a series of
 450  
      * lowercase characters. </p>
 451  
      *
 452  
      * <p>The delimiters represent a set of characters understood to separate words.
 453  
      * The first string character and the first non-delimiter character after a
 454  
      * delimiter will be capitalized. </p>
 455  
      *
 456  
      * <p>A <code>null</code> input String returns <code>null</code>.
 457  
      * Capitalization uses the Unicode title case, normally equivalent to
 458  
      * upper case.</p>
 459  
      *
 460  
      * <pre>
 461  
      * WordUtils.capitalizeFully(null, *)            = null
 462  
      * WordUtils.capitalizeFully("", *)              = ""
 463  
      * WordUtils.capitalizeFully(*, null)            = *
 464  
      * WordUtils.capitalizeFully(*, new char[0])     = *
 465  
      * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
 466  
      * </pre>
 467  
      *
 468  
      * @param str  the String to capitalize, may be null
 469  
      * @param delimiters  set of characters to determine capitalization, null means whitespace
 470  
      * @return capitalized String, <code>null</code> if null String input
 471  
      * @since 2.1
 472  
      */
 473  
     public static String capitalizeFully(String str, final char... delimiters) {
 474  20
         final int delimLen = delimiters == null ? -1 : delimiters.length;
 475  20
         if (StringUtils.isEmpty(str) || delimLen == 0) {
 476  5
             return str;
 477  
         }
 478  15
         str = str.toLowerCase();
 479  15
         return capitalize(str, delimiters);
 480  
     }
 481  
 
 482  
     //-----------------------------------------------------------------------
 483  
     /**
 484  
      * <p>Uncapitalizes all the whitespace separated words in a String.
 485  
      * Only the first character of each word is changed.</p>
 486  
      *
 487  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 488  
      * A <code>null</code> input String returns <code>null</code>.</p>
 489  
      *
 490  
      * <pre>
 491  
      * WordUtils.uncapitalize(null)        = null
 492  
      * WordUtils.uncapitalize("")          = ""
 493  
      * WordUtils.uncapitalize("I Am FINE") = "i am fINE"
 494  
      * </pre>
 495  
      *
 496  
      * @param str  the String to uncapitalize, may be null
 497  
      * @return uncapitalized String, <code>null</code> if null String input
 498  
      * @see #capitalize(String)
 499  
      */
 500  
     public static String uncapitalize(final String str) {
 501  9
         return uncapitalize(str, null);
 502  
     }
 503  
 
 504  
     /**
 505  
      * <p>Uncapitalizes all the whitespace separated words in a String.
 506  
      * Only the first character of each word is changed.</p>
 507  
      *
 508  
      * <p>The delimiters represent a set of characters understood to separate words.
 509  
      * The first string character and the first non-delimiter character after a
 510  
      * delimiter will be uncapitalized. </p>
 511  
      *
 512  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 513  
      * A <code>null</code> input String returns <code>null</code>.</p>
 514  
      *
 515  
      * <pre>
 516  
      * WordUtils.uncapitalize(null, *)            = null
 517  
      * WordUtils.uncapitalize("", *)              = ""
 518  
      * WordUtils.uncapitalize(*, null)            = *
 519  
      * WordUtils.uncapitalize(*, new char[0])     = *
 520  
      * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
 521  
      * </pre>
 522  
      *
 523  
      * @param str  the String to uncapitalize, may be null
 524  
      * @param delimiters  set of characters to determine uncapitalization, null means whitespace
 525  
      * @return uncapitalized String, <code>null</code> if null String input
 526  
      * @see #capitalize(String)
 527  
      * @since 2.1
 528  
      */
 529  
     public static String uncapitalize(final String str, final char... delimiters) {
 530  20
         final int delimLen = delimiters == null ? -1 : delimiters.length;
 531  20
         if (StringUtils.isEmpty(str) || delimLen == 0) {
 532  5
             return str;
 533  
         }
 534  15
         final char[] buffer = str.toCharArray();
 535  15
         boolean uncapitalizeNext = true;
 536  143
         for (int i = 0; i < buffer.length; i++) {
 537  128
             final char ch = buffer[i];
 538  128
             if (isDelimiter(ch, delimiters)) {
 539  28
                 uncapitalizeNext = true;
 540  100
             } else if (uncapitalizeNext) {
 541  40
                 buffer[i] = Character.toLowerCase(ch);
 542  40
                 uncapitalizeNext = false;
 543  
             }
 544  
         }
 545  15
         return new String(buffer);
 546  
     }
 547  
 
 548  
     //-----------------------------------------------------------------------
 549  
     /**
 550  
      * <p>Swaps the case of a String using a word based algorithm.</p>
 551  
      *
 552  
      * <ul>
 553  
      *  <li>Upper case character converts to Lower case</li>
 554  
      *  <li>Title case character converts to Lower case</li>
 555  
      *  <li>Lower case character after Whitespace or at start converts to Title case</li>
 556  
      *  <li>Other Lower case character converts to Upper case</li>
 557  
      * </ul>
 558  
      *
 559  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 560  
      * A <code>null</code> input String returns <code>null</code>.</p>
 561  
      *
 562  
      * <pre>
 563  
      * StringUtils.swapCase(null)                 = null
 564  
      * StringUtils.swapCase("")                   = ""
 565  
      * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
 566  
      * </pre>
 567  
      *
 568  
      * @param str  the String to swap case, may be null
 569  
      * @return the changed String, <code>null</code> if null String input
 570  
      */
 571  
     public static String swapCase(final String str) {
 572  13
         if (StringUtils.isEmpty(str)) {
 573  2
             return str;
 574  
         }
 575  11
         final char[] buffer = str.toCharArray();
 576  
 
 577  11
         boolean whitespace = true;
 578  
 
 579  159
         for (int i = 0; i < buffer.length; i++) {
 580  148
             final char ch = buffer[i];
 581  148
             if (Character.isUpperCase(ch)) {
 582  24
                 buffer[i] = Character.toLowerCase(ch);
 583  24
                 whitespace = false;
 584  124
             } else if (Character.isTitleCase(ch)) {
 585  2
                 buffer[i] = Character.toLowerCase(ch);
 586  2
                 whitespace = false;
 587  122
             } else if (Character.isLowerCase(ch)) {
 588  82
                 if (whitespace) {
 589  13
                     buffer[i] = Character.toTitleCase(ch);
 590  13
                     whitespace = false;
 591  
                 } else {
 592  69
                     buffer[i] = Character.toUpperCase(ch);
 593  
                 }
 594  
             } else {
 595  40
                 whitespace = Character.isWhitespace(ch);
 596  
             }
 597  
         }
 598  11
         return new String(buffer);
 599  
     }
 600  
 
 601  
     //-----------------------------------------------------------------------
 602  
     /**
 603  
      * <p>Extracts the initial characters from each word in the String.</p>
 604  
      *
 605  
      * <p>All first characters after whitespace are returned as a new string.
 606  
      * Their case is not changed.</p>
 607  
      *
 608  
      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
 609  
      * A <code>null</code> input String returns <code>null</code>.</p>
 610  
      *
 611  
      * <pre>
 612  
      * WordUtils.initials(null)             = null
 613  
      * WordUtils.initials("")               = ""
 614  
      * WordUtils.initials("Ben John Lee")   = "BJL"
 615  
      * WordUtils.initials("Ben J.Lee")      = "BJ"
 616  
      * </pre>
 617  
      *
 618  
      * @param str  the String to get initials from, may be null
 619  
      * @return String of initial letters, <code>null</code> if null String input
 620  
      * @see #initials(String,char[])
 621  
      * @since 2.2
 622  
      */
 623  
     public static String initials(final String str) {
 624  10
         return initials(str, null);
 625  
     }
 626  
 
 627  
     /**
 628  
      * <p>Extracts the initial characters from each word in the String.</p>
 629  
      *
 630  
      * <p>All first characters after the defined delimiters are returned as a new string.
 631  
      * Their case is not changed.</p>
 632  
      *
 633  
      * <p>If the delimiters array is null, then Whitespace is used.
 634  
      * Whitespace is defined by {@link Character#isWhitespace(char)}.
 635  
      * A <code>null</code> input String returns <code>null</code>.
 636  
      * An empty delimiter array returns an empty String.</p>
 637  
      *
 638  
      * <pre>
 639  
      * WordUtils.initials(null, *)                = null
 640  
      * WordUtils.initials("", *)                  = ""
 641  
      * WordUtils.initials("Ben John Lee", null)   = "BJL"
 642  
      * WordUtils.initials("Ben J.Lee", null)      = "BJ"
 643  
      * WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
 644  
      * WordUtils.initials(*, new char[0])         = ""
 645  
      * </pre>
 646  
      *
 647  
      * @param str  the String to get initials from, may be null
 648  
      * @param delimiters  set of characters to determine words, null means whitespace
 649  
      * @return String of initial characters, <code>null</code> if null String input
 650  
      * @see #initials(String)
 651  
      * @since 2.2
 652  
      */
 653  
     public static String initials(final String str, final char... delimiters) {
 654  79
         if (StringUtils.isEmpty(str)) {
 655  14
             return str;
 656  
         }
 657  65
         if (delimiters != null && delimiters.length == 0) {
 658  10
             return StringUtils.EMPTY;
 659  
         }
 660  55
         final int strLen = str.length();
 661  55
         final char[] buf = new char[strLen / 2 + 1];
 662  55
         int count = 0;
 663  55
         boolean lastWasGap = true;
 664  526
         for (int i = 0; i < strLen; i++) {
 665  471
             final char ch = str.charAt(i);
 666  
 
 667  471
             if (isDelimiter(ch, delimiters)) {
 668  120
                 lastWasGap = true;
 669  351
             } else if (lastWasGap) {
 670  109
                 buf[count++] = ch;
 671  109
                 lastWasGap = false;
 672  
             } else {
 673  
                 continue; // ignore ch
 674  
             }
 675  
         }
 676  55
         return new String(buf, 0, count);
 677  
     }
 678  
 
 679  
     //-----------------------------------------------------------------------
 680  
     /**
 681  
      * <p>Checks if the String contains all words in the given array.</p>
 682  
      *
 683  
      * <p>
 684  
      * A {@code null} String will return {@code false}. A {@code null}, zero
 685  
      * length search array or if one element of array is null will return {@code false}.
 686  
      * </p>
 687  
      *
 688  
      * <pre>
 689  
      * WordUtils.containsAllWords(null, *)            = false
 690  
      * WordUtils.containsAllWords("", *)              = false
 691  
      * WordUtils.containsAllWords(*, null)            = false
 692  
      * WordUtils.containsAllWords(*, [])              = false
 693  
      * WordUtils.containsAllWords("abcd", "ab", "cd") = false
 694  
      * WordUtils.containsAllWords("abc def", "def", "abc") = true
 695  
      * </pre>
 696  
      *
 697  
      *
 698  
      * @param word The CharSequence to check, may be null
 699  
      * @param words The array of String words to search for, may be null
 700  
      * @return {@code true} if all search words are found, {@code false} otherwise
 701  
      * @since 3.5
 702  
      */
 703  
     public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
 704  14
         if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) {
 705  6
             return false;
 706  
         }
 707  13
         for (final CharSequence w : words) {
 708  12
             if (StringUtils.isBlank(w)) {
 709  4
                 return false;
 710  
             }
 711  8
             final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
 712  8
             if (!p.matcher(word).matches()) {
 713  3
                 return false;
 714  
             }
 715  
         }
 716  1
         return true;
 717  
     }
 718  
 
 719  
     //-----------------------------------------------------------------------
 720  
     /**
 721  
      * Is the character a delimiter.
 722  
      *
 723  
      * @param ch  the character to check
 724  
      * @param delimiters  the delimiters
 725  
      * @return true if it is a delimiter
 726  
      */
 727  
     private static boolean isDelimiter(final char ch, final char[] delimiters) {
 728  855
         if (delimiters == null) {
 729  362
             return Character.isWhitespace(ch);
 730  
         }
 731  1722
         for (final char delimiter : delimiters) {
 732  1337
             if (ch == delimiter) {
 733  108
                 return true;
 734  
             }
 735  
         }
 736  385
         return false;
 737  
     }
 738  
 
 739  
 }