WordUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.text;

  18. import java.util.regex.Matcher;
  19. import java.util.regex.Pattern;

  20. import org.apache.commons.lang3.ArrayUtils;
  21. import org.apache.commons.lang3.StringUtils;

  22. /**
  23.  * Operations on Strings that contain words.
  24.  *
  25.  * <p>This class tries to handle {@code null} input gracefully.
  26.  * An exception will not be thrown for a {@code null} input.
  27.  * Each method documents its behavior in more detail.</p>
  28.  *
  29.  * @since 2.0
  30.  * @deprecated As of 3.6, use Apache Commons Text
  31.  * <a href="https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/WordUtils.html">
  32.  * WordUtils</a> instead
  33.  */
  34. @Deprecated
  35. public class WordUtils {

  36.     // Capitalizing
  37.     /**
  38.      * Capitalizes all the whitespace separated words in a String.
  39.      * Only the first character of each word is changed. To convert the
  40.      * rest of each word to lowercase at the same time,
  41.      * use {@link #capitalizeFully(String)}.
  42.      *
  43.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  44.      * A {@code null} input String returns {@code null}.
  45.      * Capitalization uses the Unicode title case, normally equivalent to
  46.      * upper case.</p>
  47.      *
  48.      * <pre>
  49.      * WordUtils.capitalize(null)        = null
  50.      * WordUtils.capitalize("")          = ""
  51.      * WordUtils.capitalize("i am FINE") = "I Am FINE"
  52.      * </pre>
  53.      *
  54.      * @param str  the String to capitalize, may be null
  55.      * @return capitalized String, {@code null} if null String input
  56.      * @see #uncapitalize(String)
  57.      * @see #capitalizeFully(String)
  58.      */
  59.     public static String capitalize(final String str) {
  60.         return capitalize(str, null);
  61.     }

  62.     /**
  63.      * Capitalizes all the delimiter separated words in a String.
  64.      * Only the first character of each word is changed. To convert the
  65.      * rest of each word to lowercase at the same time,
  66.      * use {@link #capitalizeFully(String, char[])}.
  67.      *
  68.      * <p>The delimiters represent a set of characters understood to separate words.
  69.      * The first string character and the first non-delimiter character after a
  70.      * delimiter will be capitalized.</p>
  71.      *
  72.      * <p>A {@code null} input String returns {@code null}.
  73.      * Capitalization uses the Unicode title case, normally equivalent to
  74.      * upper case.</p>
  75.      *
  76.      * <pre>
  77.      * WordUtils.capitalize(null, *)            = null
  78.      * WordUtils.capitalize("", *)              = ""
  79.      * WordUtils.capitalize(*, new char[0])     = *
  80.      * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
  81.      * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
  82.      * </pre>
  83.      *
  84.      * @param str  the String to capitalize, may be null
  85.      * @param delimiters  set of characters to determine capitalization, null means whitespace
  86.      * @return capitalized String, {@code null} if null String input
  87.      * @see #uncapitalize(String)
  88.      * @see #capitalizeFully(String)
  89.      * @since 2.1
  90.      */
  91.     public static String capitalize(final String str, final char... delimiters) {
  92.         final int delimLen = delimiters == null ? -1 : delimiters.length;
  93.         if (StringUtils.isEmpty(str) || delimLen == 0) {
  94.             return str;
  95.         }
  96.         final char[] buffer = str.toCharArray();
  97.         boolean capitalizeNext = true;
  98.         for (int i = 0; i < buffer.length; i++) {
  99.             final char ch = buffer[i];
  100.             if (isDelimiter(ch, delimiters)) {
  101.                 capitalizeNext = true;
  102.             } else if (capitalizeNext) {
  103.                 buffer[i] = Character.toTitleCase(ch);
  104.                 capitalizeNext = false;
  105.             }
  106.         }
  107.         return new String(buffer);
  108.     }

  109.     /**
  110.      * Converts all the whitespace separated words in a String into capitalized words,
  111.      * that is each word is made up of a titlecase character and then a series of
  112.      * lowercase characters.
  113.      *
  114.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  115.      * A {@code null} input String returns {@code null}.
  116.      * Capitalization uses the Unicode title case, normally equivalent to
  117.      * upper case.</p>
  118.      *
  119.      * <pre>
  120.      * WordUtils.capitalizeFully(null)        = null
  121.      * WordUtils.capitalizeFully("")          = ""
  122.      * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
  123.      * </pre>
  124.      *
  125.      * @param str  the String to capitalize, may be null
  126.      * @return capitalized String, {@code null} if null String input
  127.      */
  128.     public static String capitalizeFully(final String str) {
  129.         return capitalizeFully(str, null);
  130.     }

  131.     /**
  132.      * Converts all the delimiter separated words in a String into capitalized words,
  133.      * that is each word is made up of a titlecase character and then a series of
  134.      * lowercase characters.
  135.      *
  136.      * <p>The delimiters represent a set of characters understood to separate words.
  137.      * The first string character and the first non-delimiter character after a
  138.      * delimiter will be capitalized.</p>
  139.      *
  140.      * <p>A {@code null} input String returns {@code null}.
  141.      * Capitalization uses the Unicode title case, normally equivalent to
  142.      * upper case.</p>
  143.      *
  144.      * <pre>
  145.      * WordUtils.capitalizeFully(null, *)            = null
  146.      * WordUtils.capitalizeFully("", *)              = ""
  147.      * WordUtils.capitalizeFully(*, null)            = *
  148.      * WordUtils.capitalizeFully(*, new char[0])     = *
  149.      * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
  150.      * </pre>
  151.      *
  152.      * @param str  the String to capitalize, may be null
  153.      * @param delimiters  set of characters to determine capitalization, null means whitespace
  154.      * @return capitalized String, {@code null} if null String input
  155.      * @since 2.1
  156.      */
  157.     public static String capitalizeFully(final String str, final char... delimiters) {
  158.         final int delimLen = delimiters == null ? -1 : delimiters.length;
  159.         if (StringUtils.isEmpty(str) || delimLen == 0) {
  160.             return str;
  161.         }
  162.         return capitalize(str.toLowerCase(), delimiters);
  163.     }

  164.     /**
  165.      * Checks if the String contains all words in the given array.
  166.      *
  167.      * <p>
  168.      * A {@code null} String will return {@code false}. A {@code null}, zero
  169.      * length search array or if one element of array is null will return {@code false}.
  170.      * </p>
  171.      *
  172.      * <pre>
  173.      * WordUtils.containsAllWords(null, *)            = false
  174.      * WordUtils.containsAllWords("", *)              = false
  175.      * WordUtils.containsAllWords(*, null)            = false
  176.      * WordUtils.containsAllWords(*, [])              = false
  177.      * WordUtils.containsAllWords("abcd", "ab", "cd") = false
  178.      * WordUtils.containsAllWords("abc def", "def", "abc") = true
  179.      * </pre>
  180.      *
  181.      * @param word The CharSequence to check, may be null
  182.      * @param words The array of String words to search for, may be null
  183.      * @return {@code true} if all search words are found, {@code false} otherwise
  184.      * @since 3.5
  185.      */
  186.     public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
  187.         if (StringUtils.isEmpty(word) || ArrayUtils.isEmpty(words)) {
  188.             return false;
  189.         }
  190.         for (final CharSequence w : words) {
  191.             if (StringUtils.isBlank(w)) {
  192.                 return false;
  193.             }
  194.             final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
  195.             if (!p.matcher(word).matches()) {
  196.                 return false;
  197.             }
  198.         }
  199.         return true;
  200.     }

  201.     /**
  202.      * Extracts the initial characters from each word in the String.
  203.      *
  204.      * <p>All first characters after whitespace are returned as a new string.
  205.      * Their case is not changed.</p>
  206.      *
  207.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  208.      * A {@code null} input String returns {@code null}.</p>
  209.      *
  210.      * <pre>
  211.      * WordUtils.initials(null)             = null
  212.      * WordUtils.initials("")               = ""
  213.      * WordUtils.initials("Ben John Lee")   = "BJL"
  214.      * WordUtils.initials("Ben J.Lee")      = "BJ"
  215.      * </pre>
  216.      *
  217.      * @param str  the String to get initials from, may be null
  218.      * @return String of initial letters, {@code null} if null String input
  219.      * @see #initials(String,char[])
  220.      * @since 2.2
  221.      */
  222.     public static String initials(final String str) {
  223.         return initials(str, null);
  224.     }

  225.     /**
  226.      * Extracts the initial characters from each word in the String.
  227.      *
  228.      * <p>All first characters after the defined delimiters are returned as a new string.
  229.      * Their case is not changed.</p>
  230.      *
  231.      * <p>If the delimiters array is null, then Whitespace is used.
  232.      * Whitespace is defined by {@link Character#isWhitespace(char)}.
  233.      * A {@code null} input String returns {@code null}.
  234.      * An empty delimiter array returns an empty String.</p>
  235.      *
  236.      * <pre>
  237.      * WordUtils.initials(null, *)                = null
  238.      * WordUtils.initials("", *)                  = ""
  239.      * WordUtils.initials("Ben John Lee", null)   = "BJL"
  240.      * WordUtils.initials("Ben J.Lee", null)      = "BJ"
  241.      * WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
  242.      * WordUtils.initials(*, new char[0])         = ""
  243.      * </pre>
  244.      *
  245.      * @param str  the String to get initials from, may be null
  246.      * @param delimiters  set of characters to determine words, null means whitespace
  247.      * @return String of initial characters, {@code null} if null String input
  248.      * @see #initials(String)
  249.      * @since 2.2
  250.      */
  251.     public static String initials(final String str, final char... delimiters) {
  252.         if (StringUtils.isEmpty(str)) {
  253.             return str;
  254.         }
  255.         if (delimiters != null && delimiters.length == 0) {
  256.             return StringUtils.EMPTY;
  257.         }
  258.         final int strLen = str.length();
  259.         final char[] buf = new char[strLen / 2 + 1];
  260.         int count = 0;
  261.         boolean lastWasGap = true;
  262.         for (int i = 0; i < strLen; i++) {
  263.             final char ch = str.charAt(i);
  264.             if (isDelimiter(ch, delimiters)) {
  265.                 lastWasGap = true;
  266.             } else if (lastWasGap) {
  267.                 buf[count++] = ch;
  268.                 lastWasGap = false;
  269.             } else {
  270.                 continue; // ignore ch
  271.             }
  272.         }
  273.         return new String(buf, 0, count);
  274.     }

  275.     /**
  276.      * Tests if the character is a delimiter.
  277.      *
  278.      * @param ch  the character to check
  279.      * @param delimiters  the delimiters
  280.      * @return true if it is a delimiter
  281.      */
  282.     private static boolean isDelimiter(final char ch, final char[] delimiters) {
  283.         return delimiters == null ? Character.isWhitespace(ch) : ArrayUtils.contains(delimiters, ch);
  284.     }

  285.     /**
  286.      * Swaps the case of a String using a word based algorithm.
  287.      *
  288.      * <ul>
  289.      *  <li>Upper case character converts to Lower case</li>
  290.      *  <li>Title case character converts to Lower case</li>
  291.      *  <li>Lower case character after Whitespace or at start converts to Title case</li>
  292.      *  <li>Other Lower case character converts to Upper case</li>
  293.      * </ul>
  294.      *
  295.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  296.      * A {@code null} input String returns {@code null}.</p>
  297.      *
  298.      * <pre>
  299.      * StringUtils.swapCase(null)                 = null
  300.      * StringUtils.swapCase("")                   = ""
  301.      * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
  302.      * </pre>
  303.      *
  304.      * @param str  the String to swap case, may be null
  305.      * @return the changed String, {@code null} if null String input
  306.      */
  307.     public static String swapCase(final String str) {
  308.         if (StringUtils.isEmpty(str)) {
  309.             return str;
  310.         }
  311.         final char[] buffer = str.toCharArray();

  312.         boolean whitespace = true;

  313.         for (int i = 0; i < buffer.length; i++) {
  314.             final char ch = buffer[i];
  315.             if (Character.isUpperCase(ch) || Character.isTitleCase(ch)) {
  316.                 buffer[i] = Character.toLowerCase(ch);
  317.                 whitespace = false;
  318.             } else if (Character.isLowerCase(ch)) {
  319.                 if (whitespace) {
  320.                     buffer[i] = Character.toTitleCase(ch);
  321.                     whitespace = false;
  322.                 } else {
  323.                     buffer[i] = Character.toUpperCase(ch);
  324.                 }
  325.             } else {
  326.                 whitespace = Character.isWhitespace(ch);
  327.             }
  328.         }
  329.         return new String(buffer);
  330.     }

  331.     /**
  332.      * Uncapitalizes all the whitespace separated words in a String.
  333.      * Only the first character of each word is changed.
  334.      *
  335.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  336.      * A {@code null} input String returns {@code null}.</p>
  337.      *
  338.      * <pre>
  339.      * WordUtils.uncapitalize(null)        = null
  340.      * WordUtils.uncapitalize("")          = ""
  341.      * WordUtils.uncapitalize("I Am FINE") = "i am fINE"
  342.      * </pre>
  343.      *
  344.      * @param str  the String to uncapitalize, may be null
  345.      * @return uncapitalized String, {@code null} if null String input
  346.      * @see #capitalize(String)
  347.      */
  348.     public static String uncapitalize(final String str) {
  349.         return uncapitalize(str, null);
  350.     }

  351.     /**
  352.      * Uncapitalizes all the whitespace separated words in a String.
  353.      * Only the first character of each word is changed.
  354.      *
  355.      * <p>The delimiters represent a set of characters understood to separate words.
  356.      * The first string character and the first non-delimiter character after a
  357.      * delimiter will be uncapitalized.</p>
  358.      *
  359.      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
  360.      * A {@code null} input String returns {@code null}.</p>
  361.      *
  362.      * <pre>
  363.      * WordUtils.uncapitalize(null, *)            = null
  364.      * WordUtils.uncapitalize("", *)              = ""
  365.      * WordUtils.uncapitalize(*, null)            = *
  366.      * WordUtils.uncapitalize(*, new char[0])     = *
  367.      * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
  368.      * </pre>
  369.      *
  370.      * @param str  the String to uncapitalize, may be null
  371.      * @param delimiters  set of characters to determine uncapitalization, null means whitespace
  372.      * @return uncapitalized String, {@code null} if null String input
  373.      * @see #capitalize(String)
  374.      * @since 2.1
  375.      */
  376.     public static String uncapitalize(final String str, final char... delimiters) {
  377.         final int delimLen = delimiters == null ? -1 : delimiters.length;
  378.         if (StringUtils.isEmpty(str) || delimLen == 0) {
  379.             return str;
  380.         }
  381.         final char[] buffer = str.toCharArray();
  382.         boolean uncapitalizeNext = true;
  383.         for (int i = 0; i < buffer.length; i++) {
  384.             final char ch = buffer[i];
  385.             if (isDelimiter(ch, delimiters)) {
  386.                 uncapitalizeNext = true;
  387.             } else if (uncapitalizeNext) {
  388.                 buffer[i] = Character.toLowerCase(ch);
  389.                 uncapitalizeNext = false;
  390.             }
  391.         }
  392.         return new String(buffer);
  393.     }

  394.     /**
  395.      * Wraps a single line of text, identifying words by {@code ' '}.
  396.      *
  397.      * <p>New lines will be separated by the system property line separator.
  398.      * Very long words, such as URLs will <em>not</em> be wrapped.</p>
  399.      *
  400.      * <p>Leading spaces on a new line are stripped.
  401.      * Trailing spaces are not stripped.</p>
  402.      *
  403.      * <table border="1">
  404.      *  <caption>Examples</caption>
  405.      *  <tr>
  406.      *   <th>input</th>
  407.      *   <th>wrapLength</th>
  408.      *   <th>result</th>
  409.      *  </tr>
  410.      *  <tr>
  411.      *   <td>null</td>
  412.      *   <td>*</td>
  413.      *   <td>null</td>
  414.      *  </tr>
  415.      *  <tr>
  416.      *   <td>""</td>
  417.      *   <td>*</td>
  418.      *   <td>""</td>
  419.      *  </tr>
  420.      *  <tr>
  421.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  422.      *   <td>20</td>
  423.      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
  424.      *  </tr>
  425.      *  <tr>
  426.      *   <td>"Click here to jump to the commons website - https://commons.apache.org"</td>
  427.      *   <td>20</td>
  428.      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttps://commons.apache.org"</td>
  429.      *  </tr>
  430.      *  <tr>
  431.      *   <td>"Click here, https://commons.apache.org, to jump to the commons website"</td>
  432.      *   <td>20</td>
  433.      *   <td>"Click here,\nhttps://commons.apache.org,\nto jump to the\ncommons website"</td>
  434.      *  </tr>
  435.      * </table>
  436.      *
  437.      * (assuming that '\n' is the systems line separator)
  438.      *
  439.      * @param str  the String to be word wrapped, may be null
  440.      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
  441.      * @return a line with newlines inserted, {@code null} if null input
  442.      */
  443.     public static String wrap(final String str, final int wrapLength) {
  444.         return wrap(str, wrapLength, null, false);
  445.     }

  446.     /**
  447.      * Wraps a single line of text, identifying words by {@code ' '}.
  448.      *
  449.      * <p>Leading spaces on a new line are stripped.
  450.      * Trailing spaces are not stripped.</p>
  451.      *
  452.      * <table border="1">
  453.      *  <caption>Examples</caption>
  454.      *  <tr>
  455.      *   <th>input</th>
  456.      *   <th>wrapLength</th>
  457.      *   <th>newLineString</th>
  458.      *   <th>wrapLongWords</th>
  459.      *   <th>result</th>
  460.      *  </tr>
  461.      *  <tr>
  462.      *   <td>null</td>
  463.      *   <td>*</td>
  464.      *   <td>*</td>
  465.      *   <td>true/false</td>
  466.      *   <td>null</td>
  467.      *  </tr>
  468.      *  <tr>
  469.      *   <td>""</td>
  470.      *   <td>*</td>
  471.      *   <td>*</td>
  472.      *   <td>true/false</td>
  473.      *   <td>""</td>
  474.      *  </tr>
  475.      *  <tr>
  476.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  477.      *   <td>20</td>
  478.      *   <td>"\n"</td>
  479.      *   <td>true/false</td>
  480.      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
  481.      *  </tr>
  482.      *  <tr>
  483.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  484.      *   <td>20</td>
  485.      *   <td>"&lt;br /&gt;"</td>
  486.      *   <td>true/false</td>
  487.      *   <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
  488.      *  </tr>
  489.      *  <tr>
  490.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  491.      *   <td>20</td>
  492.      *   <td>null</td>
  493.      *   <td>true/false</td>
  494.      *   <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
  495.      *  </tr>
  496.      *  <tr>
  497.      *   <td>"Click here to jump to the commons website - https://commons.apache.org"</td>
  498.      *   <td>20</td>
  499.      *   <td>"\n"</td>
  500.      *   <td>false</td>
  501.      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttps://commons.apache.org"</td>
  502.      *  </tr>
  503.      *  <tr>
  504.      *   <td>"Click here to jump to the commons website - https://commons.apache.org"</td>
  505.      *   <td>20</td>
  506.      *   <td>"\n"</td>
  507.      *   <td>true</td>
  508.      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttps://commons.apach\ne.org"</td>
  509.      *  </tr>
  510.      * </table>
  511.      *
  512.      * @param str  the String to be word wrapped, may be null
  513.      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
  514.      * @param newLineStr  the string to insert for a new line,
  515.      *  {@code null} uses the system property line separator
  516.      * @param wrapLongWords  true if long words (such as URLs) should be wrapped
  517.      * @return a line with newlines inserted, {@code null} if null input
  518.      */
  519.     public static String wrap(final String str, final int wrapLength, final String newLineStr, final boolean wrapLongWords) {
  520.         return wrap(str, wrapLength, newLineStr, wrapLongWords, " ");
  521.     }

  522.     /**
  523.      * Wraps a single line of text, identifying words by {@code wrapOn}.
  524.      *
  525.      * <p>Leading spaces on a new line are stripped.
  526.      * Trailing spaces are not stripped.</p>
  527.      *
  528.      * <table border="1">
  529.      *  <caption>Examples</caption>
  530.      *  <tr>
  531.      *   <th>input</th>
  532.      *   <th>wrapLength</th>
  533.      *   <th>newLineString</th>
  534.      *   <th>wrapLongWords</th>
  535.      *   <th>wrapOn</th>
  536.      *   <th>result</th>
  537.      *  </tr>
  538.      *  <tr>
  539.      *   <td>null</td>
  540.      *   <td>*</td>
  541.      *   <td>*</td>
  542.      *   <td>true/false</td>
  543.      *   <td>*</td>
  544.      *   <td>null</td>
  545.      *  </tr>
  546.      *  <tr>
  547.      *   <td>""</td>
  548.      *   <td>*</td>
  549.      *   <td>*</td>
  550.      *   <td>true/false</td>
  551.      *   <td>*</td>
  552.      *   <td>""</td>
  553.      *  </tr>
  554.      *  <tr>
  555.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  556.      *   <td>20</td>
  557.      *   <td>"\n"</td>
  558.      *   <td>true/false</td>
  559.      *   <td>" "</td>
  560.      *   <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td>
  561.      *  </tr>
  562.      *  <tr>
  563.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  564.      *   <td>20</td>
  565.      *   <td>"&lt;br /&gt;"</td>
  566.      *   <td>true/false</td>
  567.      *   <td>" "</td>
  568.      *   <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be wrapped after&lt;br /&gt;20 columns."</td>
  569.      *  </tr>
  570.      *  <tr>
  571.      *   <td>"Here is one line of text that is going to be wrapped after 20 columns."</td>
  572.      *   <td>20</td>
  573.      *   <td>null</td>
  574.      *   <td>true/false</td>
  575.      *   <td>" "</td>
  576.      *   <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
  577.      *  </tr>
  578.      *  <tr>
  579.      *   <td>"Click here to jump to the commons website - https://commons.apache.org"</td>
  580.      *   <td>20</td>
  581.      *   <td>"\n"</td>
  582.      *   <td>false</td>
  583.      *   <td>" "</td>
  584.      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttps://commons.apache.org"</td>
  585.      *  </tr>
  586.      *  <tr>
  587.      *   <td>"Click here to jump to the commons website - https://commons.apache.org"</td>
  588.      *   <td>20</td>
  589.      *   <td>"\n"</td>
  590.      *   <td>true</td>
  591.      *   <td>" "</td>
  592.      *   <td>"Click here to jump\nto the commons\nwebsite -\nhttps://commons.apach\ne.org"</td>
  593.      *  </tr>
  594.      *  <tr>
  595.      *   <td>"flammable/inflammable"</td>
  596.      *   <td>20</td>
  597.      *   <td>"\n"</td>
  598.      *   <td>true</td>
  599.      *   <td>"/"</td>
  600.      *   <td>"flammable\ninflammable"</td>
  601.      *  </tr>
  602.      * </table>
  603.      * @param str  the String to be word wrapped, may be null
  604.      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
  605.      * @param newLineStr  the string to insert for a new line,
  606.      *  {@code null} uses the system property line separator
  607.      * @param wrapLongWords  true if long words (such as URLs) should be wrapped
  608.      * @param wrapOn regex expression to be used as a breakable characters,
  609.      *               if blank string is provided a space character will be used
  610.      * @return a line with newlines inserted, {@code null} if null input
  611.      */
  612.     public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) {
  613.         if (str == null) {
  614.             return null;
  615.         }
  616.         if (newLineStr == null) {
  617.             newLineStr = System.lineSeparator();
  618.         }
  619.         if (wrapLength < 1) {
  620.             wrapLength = 1;
  621.         }
  622.         if (StringUtils.isBlank(wrapOn)) {
  623.             wrapOn = " ";
  624.         }
  625.         final Pattern patternToWrapOn = Pattern.compile(wrapOn);
  626.         final int inputLineLength = str.length();
  627.         int offset = 0;
  628.         final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32);

  629.         while (offset < inputLineLength) {
  630.             int spaceToWrapAt = -1;
  631.             Matcher matcher = patternToWrapOn.matcher(
  632.                 str.substring(offset, Math.min((int) Math.min(Integer.MAX_VALUE, offset + wrapLength + 1L), inputLineLength)));
  633.             if (matcher.find()) {
  634.                 if (matcher.start() == 0) {
  635.                     offset += matcher.end();
  636.                     continue;
  637.                 }
  638.                 spaceToWrapAt = matcher.start() + offset;
  639.             }

  640.             // only last line without leading spaces is left
  641.             if (inputLineLength - offset <= wrapLength) {
  642.                 break;
  643.             }

  644.             while (matcher.find()) {
  645.                 spaceToWrapAt = matcher.start() + offset;
  646.             }

  647.             if (spaceToWrapAt >= offset) {
  648.                 // normal case
  649.                 wrappedLine.append(str, offset, spaceToWrapAt);
  650.                 wrappedLine.append(newLineStr);
  651.                 offset = spaceToWrapAt + 1;

  652.             } else // really long word or URL
  653.             if (wrapLongWords) {
  654.                 // wrap really long word one line at a time
  655.                 wrappedLine.append(str, offset, wrapLength + offset);
  656.                 wrappedLine.append(newLineStr);
  657.                 offset += wrapLength;
  658.             } else {
  659.                 // do not wrap really long word, just extend beyond limit
  660.                 matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength));
  661.                 if (matcher.find()) {
  662.                     spaceToWrapAt = matcher.start() + offset + wrapLength;
  663.                 }

  664.                 if (spaceToWrapAt >= 0) {
  665.                     wrappedLine.append(str, offset, spaceToWrapAt);
  666.                     wrappedLine.append(newLineStr);
  667.                     offset = spaceToWrapAt + 1;
  668.                 } else {
  669.                     wrappedLine.append(str, offset, str.length());
  670.                     offset = inputLineLength;
  671.                 }
  672.             }
  673.         }

  674.         // Whatever is left in line is short enough to just pass through
  675.         wrappedLine.append(str, offset, str.length());

  676.         return wrappedLine.toString();
  677.     }

  678.     /**
  679.      * {@link WordUtils} instances should NOT be constructed in
  680.      * standard programming. Instead, the class should be used as
  681.      * {@code WordUtils.wrap("foo bar", 20);}.
  682.      *
  683.      * <p>This constructor is public to permit tools that require a JavaBean
  684.      * instance to operate.</p>
  685.      */
  686.     public WordUtils() {
  687.     }

  688. }