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