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