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; 024import org.apache.commons.lang3.SystemUtils; 025 026/** 027 * <p>Operations on Strings that contain words.</p> 028 * 029 * <p>This class tries to handle <code>null</code> input gracefully. 030 * An exception will not be thrown for a <code>null</code> input. 031 * Each method documents its behaviour in more detail.</p> 032 * 033 * @since 2.0 034 */ 035public class WordUtils { 036 037 /** 038 * <p><code>WordUtils</code> instances should NOT be constructed in 039 * standard programming. Instead, the class should be used as 040 * <code>WordUtils.wrap("foo bar", 20);</code>.</p> 041 * 042 * <p>This constructor is public to permit tools that require a JavaBean 043 * instance to operate.</p> 044 */ 045 public WordUtils() { 046 super(); 047 } 048 049 // Wrapping 050 //-------------------------------------------------------------------------- 051 /** 052 * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p> 053 * 054 * <p>New lines will be separated by the system property line separator. 055 * Very long words, such as URLs will <i>not</i> be wrapped.</p> 056 * 057 * <p>Leading spaces on a new line are stripped. 058 * Trailing spaces are not stripped.</p> 059 * 060 * <table border="1" summary="Wrap Results"> 061 * <tr> 062 * <th>input</th> 063 * <th>wrapLength</th> 064 * <th>result</th> 065 * </tr> 066 * <tr> 067 * <td>null</td> 068 * <td>*</td> 069 * <td>null</td> 070 * </tr> 071 * <tr> 072 * <td>""</td> 073 * <td>*</td> 074 * <td>""</td> 075 * </tr> 076 * <tr> 077 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 078 * <td>20</td> 079 * <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td> 080 * </tr> 081 * <tr> 082 * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> 083 * <td>20</td> 084 * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td> 085 * </tr> 086 * <tr> 087 * <td>"Click here, http://commons.apache.org, to jump to the commons website"</td> 088 * <td>20</td> 089 * <td>"Click here,\nhttp://commons.apache.org,\nto jump to the\ncommons website"</td> 090 * </tr> 091 * </table> 092 * 093 * (assuming that '\n' is the systems line separator) 094 * 095 * @param str the String to be word wrapped, may be null 096 * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 097 * @return a line with newlines inserted, <code>null</code> if null input 098 */ 099 public static String wrap(final String str, final int wrapLength) { 100 return wrap(str, wrapLength, null, false); 101 } 102 103 /** 104 * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p> 105 * 106 * <p>Leading spaces on a new line are stripped. 107 * Trailing spaces are not stripped.</p> 108 * 109 * <table border="1" summary="Wrap Results"> 110 * <tr> 111 * <th>input</th> 112 * <th>wrapLenght</th> 113 * <th>newLineString</th> 114 * <th>wrapLongWords</th> 115 * <th>result</th> 116 * </tr> 117 * <tr> 118 * <td>null</td> 119 * <td>*</td> 120 * <td>*</td> 121 * <td>true/false</td> 122 * <td>null</td> 123 * </tr> 124 * <tr> 125 * <td>""</td> 126 * <td>*</td> 127 * <td>*</td> 128 * <td>true/false</td> 129 * <td>""</td> 130 * </tr> 131 * <tr> 132 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 133 * <td>20</td> 134 * <td>"\n"</td> 135 * <td>true/false</td> 136 * <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td> 137 * </tr> 138 * <tr> 139 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 140 * <td>20</td> 141 * <td>"<br />"</td> 142 * <td>true/false</td> 143 * <td>"Here is one line of<br />text that is going<br />to be wrapped after<br />20 columns."</td> 144 * </tr> 145 * <tr> 146 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 147 * <td>20</td> 148 * <td>null</td> 149 * <td>true/false</td> 150 * <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td> 151 * </tr> 152 * <tr> 153 * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> 154 * <td>20</td> 155 * <td>"\n"</td> 156 * <td>false</td> 157 * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td> 158 * </tr> 159 * <tr> 160 * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> 161 * <td>20</td> 162 * <td>"\n"</td> 163 * <td>true</td> 164 * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td> 165 * </tr> 166 * </table> 167 * 168 * @param str the String to be word wrapped, may be null 169 * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 170 * @param newLineStr the string to insert for a new line, 171 * <code>null</code> uses the system property line separator 172 * @param wrapLongWords true if long words (such as URLs) should be wrapped 173 * @return a line with newlines inserted, <code>null</code> if null input 174 */ 175 public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords) { 176 return wrap(str, wrapLength, newLineStr, wrapLongWords, " "); 177 } 178 179 /** 180 * <p>Wraps a single line of text, identifying words by <code>wrapOn</code>.</p> 181 * 182 * <p>Leading spaces on a new line are stripped. 183 * Trailing spaces are not stripped.</p> 184 * 185 * <table border="1" summary="Wrap Results"> 186 * <tr> 187 * <th>input</th> 188 * <th>wrapLenght</th> 189 * <th>newLineString</th> 190 * <th>wrapLongWords</th> 191 * <th>wrapOn</th> 192 * <th>result</th> 193 * </tr> 194 * <tr> 195 * <td>null</td> 196 * <td>*</td> 197 * <td>*</td> 198 * <td>true/false</td> 199 * <td>*</td> 200 * <td>null</td> 201 * </tr> 202 * <tr> 203 * <td>""</td> 204 * <td>*</td> 205 * <td>*</td> 206 * <td>true/false</td> 207 * <td>*</td> 208 * <td>""</td> 209 * </tr> 210 * <tr> 211 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 212 * <td>20</td> 213 * <td>"\n"</td> 214 * <td>true/false</td> 215 * <td>" "</td> 216 * <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td> 217 * </tr> 218 * <tr> 219 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 220 * <td>20</td> 221 * <td>"<br />"</td> 222 * <td>true/false</td> 223 * <td>" "</td> 224 * <td>"Here is one line of<br />text that is going<br />to be wrapped after<br />20 columns."</td> 225 * </tr> 226 * <tr> 227 * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> 228 * <td>20</td> 229 * <td>null</td> 230 * <td>true/false</td> 231 * <td>" "</td> 232 * <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td> 233 * </tr> 234 * <tr> 235 * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> 236 * <td>20</td> 237 * <td>"\n"</td> 238 * <td>false</td> 239 * <td>" "</td> 240 * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td> 241 * </tr> 242 * <tr> 243 * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> 244 * <td>20</td> 245 * <td>"\n"</td> 246 * <td>true</td> 247 * <td>" "</td> 248 * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td> 249 * </tr> 250 * <tr> 251 * <td>"flammable/inflammable"</td> 252 * <td>20</td> 253 * <td>"\n"</td> 254 * <td>true</td> 255 * <td>"/"</td> 256 * <td>"flammable\ninflammable"</td> 257 * </tr> 258 * </table> 259 * @param str the String to be word wrapped, may be null 260 * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 261 * @param newLineStr the string to insert for a new line, 262 * <code>null</code> uses the system property line separator 263 * @param wrapLongWords true if long words (such as URLs) should be wrapped 264 * @param wrapOn regex expression to be used as a breakable characters, 265 * if blank string is provided a space character will be used 266 * @return a line with newlines inserted, <code>null</code> if null input 267 */ 268 public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) { 269 if (str == null) { 270 return null; 271 } 272 if (newLineStr == null) { 273 newLineStr = SystemUtils.LINE_SEPARATOR; 274 } 275 if (wrapLength < 1) { 276 wrapLength = 1; 277 } 278 if (StringUtils.isBlank(wrapOn)) { 279 wrapOn = " "; 280 } 281 Pattern patternToWrapOn = Pattern.compile(wrapOn); 282 final int inputLineLength = str.length(); 283 int offset = 0; 284 final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32); 285 286 while (offset < inputLineLength) { 287 int spaceToWrapAt = -1; 288 Matcher matcher = patternToWrapOn.matcher(str.substring(offset, Math.min(offset + wrapLength + 1, inputLineLength))); 289 if (matcher.find()) { 290 if (matcher.start() == 0) { 291 offset += matcher.end(); 292 continue; 293 }else { 294 spaceToWrapAt = matcher.start(); 295 } 296 } 297 298 // only last line without leading spaces is left 299 if(inputLineLength - offset <= wrapLength) { 300 break; 301 } 302 303 while(matcher.find()){ 304 spaceToWrapAt = matcher.start() + offset; 305 } 306 307 if (spaceToWrapAt >= offset) { 308 // normal case 309 wrappedLine.append(str.substring(offset, spaceToWrapAt)); 310 wrappedLine.append(newLineStr); 311 offset = spaceToWrapAt + 1; 312 313 } else { 314 // really long word or URL 315 if (wrapLongWords) { 316 // wrap really long word one line at a time 317 wrappedLine.append(str.substring(offset, wrapLength + offset)); 318 wrappedLine.append(newLineStr); 319 offset += wrapLength; 320 } else { 321 // do not wrap really long word, just extend beyond limit 322 matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength)); 323 if (matcher.find()) { 324 spaceToWrapAt = matcher.start() + offset + wrapLength; 325 } 326 327 if (spaceToWrapAt >= 0) { 328 wrappedLine.append(str.substring(offset, spaceToWrapAt)); 329 wrappedLine.append(newLineStr); 330 offset = spaceToWrapAt + 1; 331 } else { 332 wrappedLine.append(str.substring(offset)); 333 offset = inputLineLength; 334 } 335 } 336 } 337 } 338 339 // Whatever is left in line is short enough to just pass through 340 wrappedLine.append(str.substring(offset)); 341 342 return wrappedLine.toString(); 343 } 344 345 // Capitalizing 346 //----------------------------------------------------------------------- 347 /** 348 * <p>Capitalizes all the whitespace separated words in a String. 349 * Only the first character of each word is changed. To convert the 350 * rest of each word to lowercase at the same time, 351 * use {@link #capitalizeFully(String)}.</p> 352 * 353 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. 354 * A <code>null</code> input String returns <code>null</code>. 355 * Capitalization uses the Unicode title case, normally equivalent to 356 * upper case.</p> 357 * 358 * <pre> 359 * WordUtils.capitalize(null) = null 360 * WordUtils.capitalize("") = "" 361 * WordUtils.capitalize("i am FINE") = "I Am FINE" 362 * </pre> 363 * 364 * @param str the String to capitalize, may be null 365 * @return capitalized String, <code>null</code> if null String input 366 * @see #uncapitalize(String) 367 * @see #capitalizeFully(String) 368 */ 369 public static String capitalize(final String str) { 370 return capitalize(str, null); 371 } 372 373 /** 374 * <p>Capitalizes all the delimiter separated words in a String. 375 * Only the first character of each word is changed. To convert the 376 * rest of each word to lowercase at the same time, 377 * use {@link #capitalizeFully(String, char[])}.</p> 378 * 379 * <p>The delimiters represent a set of characters understood to separate words. 380 * The first string character and the first non-delimiter character after a 381 * delimiter will be capitalized. </p> 382 * 383 * <p>A <code>null</code> input String returns <code>null</code>. 384 * Capitalization uses the Unicode title case, normally equivalent to 385 * upper case.</p> 386 * 387 * <pre> 388 * WordUtils.capitalize(null, *) = null 389 * WordUtils.capitalize("", *) = "" 390 * WordUtils.capitalize(*, new char[0]) = * 391 * WordUtils.capitalize("i am fine", null) = "I Am Fine" 392 * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine" 393 * </pre> 394 * 395 * @param str the String to capitalize, may be null 396 * @param delimiters set of characters to determine capitalization, null means whitespace 397 * @return capitalized String, <code>null</code> if null String input 398 * @see #uncapitalize(String) 399 * @see #capitalizeFully(String) 400 * @since 2.1 401 */ 402 public static String capitalize(final String str, final char... delimiters) { 403 final int delimLen = delimiters == null ? -1 : delimiters.length; 404 if (StringUtils.isEmpty(str) || delimLen == 0) { 405 return str; 406 } 407 final char[] buffer = str.toCharArray(); 408 boolean capitalizeNext = true; 409 for (int i = 0; i < buffer.length; i++) { 410 final char ch = buffer[i]; 411 if (isDelimiter(ch, delimiters)) { 412 capitalizeNext = true; 413 } else if (capitalizeNext) { 414 buffer[i] = Character.toTitleCase(ch); 415 capitalizeNext = false; 416 } 417 } 418 return new String(buffer); 419 } 420 421 //----------------------------------------------------------------------- 422 /** 423 * <p>Converts all the whitespace separated words in a String into capitalized words, 424 * that is each word is made up of a titlecase character and then a series of 425 * lowercase characters. </p> 426 * 427 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. 428 * A <code>null</code> input String returns <code>null</code>. 429 * Capitalization uses the Unicode title case, normally equivalent to 430 * upper case.</p> 431 * 432 * <pre> 433 * WordUtils.capitalizeFully(null) = null 434 * WordUtils.capitalizeFully("") = "" 435 * WordUtils.capitalizeFully("i am FINE") = "I Am Fine" 436 * </pre> 437 * 438 * @param str the String to capitalize, may be null 439 * @return capitalized String, <code>null</code> if null String input 440 */ 441 public static String capitalizeFully(final String str) { 442 return capitalizeFully(str, null); 443 } 444 445 /** 446 * <p>Converts all the delimiter separated words in a String into capitalized words, 447 * that is each word is made up of a titlecase character and then a series of 448 * lowercase characters. </p> 449 * 450 * <p>The delimiters represent a set of characters understood to separate words. 451 * The first string character and the first non-delimiter character after a 452 * delimiter will be capitalized. </p> 453 * 454 * <p>A <code>null</code> input String returns <code>null</code>. 455 * Capitalization uses the Unicode title case, normally equivalent to 456 * upper case.</p> 457 * 458 * <pre> 459 * WordUtils.capitalizeFully(null, *) = null 460 * WordUtils.capitalizeFully("", *) = "" 461 * WordUtils.capitalizeFully(*, null) = * 462 * WordUtils.capitalizeFully(*, new char[0]) = * 463 * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine" 464 * </pre> 465 * 466 * @param str the String to capitalize, may be null 467 * @param delimiters set of characters to determine capitalization, null means whitespace 468 * @return capitalized String, <code>null</code> if null String input 469 * @since 2.1 470 */ 471 public static String capitalizeFully(String str, final char... delimiters) { 472 final int delimLen = delimiters == null ? -1 : delimiters.length; 473 if (StringUtils.isEmpty(str) || delimLen == 0) { 474 return str; 475 } 476 str = str.toLowerCase(); 477 return capitalize(str, delimiters); 478 } 479 480 //----------------------------------------------------------------------- 481 /** 482 * <p>Uncapitalizes all the whitespace separated words in a String. 483 * Only the first character of each word is changed.</p> 484 * 485 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. 486 * A <code>null</code> input String returns <code>null</code>.</p> 487 * 488 * <pre> 489 * WordUtils.uncapitalize(null) = null 490 * WordUtils.uncapitalize("") = "" 491 * WordUtils.uncapitalize("I Am FINE") = "i am fINE" 492 * </pre> 493 * 494 * @param str the String to uncapitalize, may be null 495 * @return uncapitalized String, <code>null</code> if null String input 496 * @see #capitalize(String) 497 */ 498 public static String uncapitalize(final String str) { 499 return uncapitalize(str, null); 500 } 501 502 /** 503 * <p>Uncapitalizes all the whitespace separated words in a String. 504 * Only the first character of each word is changed.</p> 505 * 506 * <p>The delimiters represent a set of characters understood to separate words. 507 * The first string character and the first non-delimiter character after a 508 * delimiter will be uncapitalized. </p> 509 * 510 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. 511 * A <code>null</code> input String returns <code>null</code>.</p> 512 * 513 * <pre> 514 * WordUtils.uncapitalize(null, *) = null 515 * WordUtils.uncapitalize("", *) = "" 516 * WordUtils.uncapitalize(*, null) = * 517 * WordUtils.uncapitalize(*, new char[0]) = * 518 * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE" 519 * </pre> 520 * 521 * @param str the String to uncapitalize, may be null 522 * @param delimiters set of characters to determine uncapitalization, null means whitespace 523 * @return uncapitalized String, <code>null</code> if null String input 524 * @see #capitalize(String) 525 * @since 2.1 526 */ 527 public static String uncapitalize(final String str, final char... delimiters) { 528 final int delimLen = delimiters == null ? -1 : delimiters.length; 529 if (StringUtils.isEmpty(str) || delimLen == 0) { 530 return str; 531 } 532 final char[] buffer = str.toCharArray(); 533 boolean uncapitalizeNext = true; 534 for (int i = 0; i < buffer.length; i++) { 535 final char ch = buffer[i]; 536 if (isDelimiter(ch, delimiters)) { 537 uncapitalizeNext = true; 538 } else if (uncapitalizeNext) { 539 buffer[i] = Character.toLowerCase(ch); 540 uncapitalizeNext = false; 541 } 542 } 543 return new String(buffer); 544 } 545 546 //----------------------------------------------------------------------- 547 /** 548 * <p>Swaps the case of a String using a word based algorithm.</p> 549 * 550 * <ul> 551 * <li>Upper case character converts to Lower case</li> 552 * <li>Title case character converts to Lower case</li> 553 * <li>Lower case character after Whitespace or at start converts to Title case</li> 554 * <li>Other Lower case character converts to Upper case</li> 555 * </ul> 556 * 557 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. 558 * A <code>null</code> input String returns <code>null</code>.</p> 559 * 560 * <pre> 561 * StringUtils.swapCase(null) = null 562 * StringUtils.swapCase("") = "" 563 * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone" 564 * </pre> 565 * 566 * @param str the String to swap case, may be null 567 * @return the changed String, <code>null</code> if null String input 568 */ 569 public static String swapCase(final String str) { 570 if (StringUtils.isEmpty(str)) { 571 return str; 572 } 573 final char[] buffer = str.toCharArray(); 574 575 boolean whitespace = true; 576 577 for (int i = 0; i < buffer.length; i++) { 578 final char ch = buffer[i]; 579 if (Character.isUpperCase(ch)) { 580 buffer[i] = Character.toLowerCase(ch); 581 whitespace = false; 582 } else if (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</code> input String returns <code>null</code>.</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</code> 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</code> input String returns <code>null</code>. 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</code> 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(CharSequence word, CharSequence... words) { 702 if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) { 703 return false; 704 } 705 for (CharSequence w : words) { 706 if (StringUtils.isBlank(w)) { 707 return false; 708 } 709 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}