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