diff --git a/src/main/java/org/apache/commons/lang3/text/WordUtils.java b/src/main/java/org/apache/commons/lang3/text/WordUtils.java index da9285688..9e04962d9 100644 --- a/src/main/java/org/apache/commons/lang3/text/WordUtils.java +++ b/src/main/java/org/apache/commons/lang3/text/WordUtils.java @@ -16,6 +16,7 @@ */ package org.apache.commons.lang3.text; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.ArrayUtils; @@ -172,6 +173,84 @@ public class WordUtils { * @return a line with newlines inserted, null if null input */ public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords) { + return wrap(str, wrapLength, newLineStr, wrapLongWords, " "); + } + + /** + *

Wraps a single line of text, identifying words by ' '.

+ * + *

Leading spaces on a new line are stripped. + * Trailing spaces are not stripped.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
inputwrapLenghtnewLineStringwrapLongWordsresult
null**true/falsenull
""**true/false""
"Here is one line of text that is going to be wrapped after 20 columns."20"\n"true/false"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."
"Here is one line of text that is going to be wrapped after 20 columns."20"<br />"true/false"Here is one line of<br />text that is going<br />to be wrapped after<br />20 columns."
"Here is one line of text that is going to be wrapped after 20 columns."20nulltrue/false"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."
"Click here to jump to the commons website - http://commons.apache.org"20"\n"false"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"
"Click here to jump to the commons website - http://commons.apache.org"20"\n"true"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"
+ * + * @param str the String to be word wrapped, may be null + * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 + * @param newLineStr the string to insert for a new line, + * null uses the system property line separator + * @param wrapLongWords true if long words (such as URLs) should be wrapped + * @param wrapOn regex expression to be used as a breakable characters, + * if blank string is provided a space character will be used + * @return a line with newlines inserted, null if null input + */ + public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) { if (str == null) { return null; } @@ -181,27 +260,41 @@ public class WordUtils { if (wrapLength < 1) { wrapLength = 1; } + if (StringUtils.isBlank(wrapOn)) { + wrapOn = " "; + } + Pattern patternToWrapOn = Pattern.compile(wrapOn); final int inputLineLength = str.length(); int offset = 0; final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32); - + while (offset < inputLineLength) { - if (str.charAt(offset) == ' ') { - offset++; - continue; + int spaceToWrapAt = -1; + Matcher matcher = patternToWrapOn.matcher(str.substring(offset, Math.min(offset + wrapLength + 1, inputLineLength))); + if (matcher.find()) { + if (matcher.start() == 0) { + offset += matcher.end(); + continue; + }else { + spaceToWrapAt = matcher.start(); + } } + // only last line without leading spaces is left if(inputLineLength - offset <= wrapLength) { break; } - int spaceToWrapAt = str.lastIndexOf(' ', wrapLength + offset); + + while(matcher.find()){ + spaceToWrapAt = matcher.start() + offset; + } if (spaceToWrapAt >= offset) { // normal case wrappedLine.append(str.substring(offset, spaceToWrapAt)); wrappedLine.append(newLineStr); offset = spaceToWrapAt + 1; - + } else { // really long word or URL if (wrapLongWords) { @@ -211,7 +304,11 @@ public class WordUtils { offset += wrapLength; } else { // do not wrap really long word, just extend beyond limit - spaceToWrapAt = str.indexOf(' ', wrapLength + offset); + matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength)); + if (matcher.find()) { + spaceToWrapAt = matcher.start() + offset + wrapLength; + } + if (spaceToWrapAt >= 0) { wrappedLine.append(str.substring(offset, spaceToWrapAt)); wrappedLine.append(newLineStr); diff --git a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java index 0da57dd58..827576812 100644 --- a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java @@ -154,7 +154,33 @@ public class WordUtilsTest { expected = "Click here,\nhttp://commons.apach\ne.org, to jump to\nthe commons website"; assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); } - + + @Test + public void testWrap_StringIntStringBooleanString() { + + //no changes test + String input = "flammable/inflammable"; + String expected = "flammable/inflammable"; + assertEquals(expected, WordUtils.wrap(input, 30, "\n", false, "/")); + + // wrap on / and small width + expected = "flammable\ninflammable"; + assertEquals(expected, WordUtils.wrap(input, 2, "\n", false, "/")); + + // wrap long words on / 1 + expected = "flammable\ninflammab\nle"; + assertEquals(expected, WordUtils.wrap(input, 9, "\n", true, "/")); + + // wrap long words on / 2 + expected = "flammable\ninflammable"; + assertEquals(expected, WordUtils.wrap(input, 15, "\n", true, "/")); + + // wrap long words on / 3 + input = "flammableinflammable"; + expected = "flammableinflam\nmable"; + assertEquals(expected, WordUtils.wrap(input, 15, "\n", true, "/")); + } + //----------------------------------------------------------------------- @Test public void testCapitalize_String() {