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