Repository: commons-lang Updated Branches: refs/heads/master cac7a60ab -> 8f362e7ea
LANG-1023 WordUtils.wrap: Customizable breakable characters (not just space/whitespace) changed wrap method to use regex pattern to find breakable characters rather than only spaces. added a few basic tests for "regex wrap" Project: http://git-wip-us.apache.org/repos/asf/commons-lang/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-lang/commit/0fe9685c Tree: http://git-wip-us.apache.org/repos/asf/commons-lang/tree/0fe9685c Diff: http://git-wip-us.apache.org/repos/asf/commons-lang/diff/0fe9685c Branch: refs/heads/master Commit: 0fe9685c8a78ba2d780a3ec1e1a323990dc6d218 Parents: bd9adbb Author: marko-bekhta <mar...@prykladna.lviv.ua> Authored: Wed Jul 27 06:21:32 2016 +0200 Committer: marko-bekhta <mar...@prykladna.lviv.ua> Committed: Wed Jul 27 06:21:32 2016 +0200 ---------------------------------------------------------------------- .../apache/commons/lang3/text/WordUtils.java | 111 +++++++++++++++++-- .../commons/lang3/text/WordUtilsTest.java | 28 ++++- 2 files changed, 131 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-lang/blob/0fe9685c/src/main/java/org/apache/commons/lang3/text/WordUtils.java ---------------------------------------------------------------------- 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 da92856..9e04962 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, <code>null</code> if null input */ public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords) { + return wrap(str, wrapLength, newLineStr, wrapLongWords, " "); + } + + /** + * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p> + * + * <p>Leading spaces on a new line are stripped. + * Trailing spaces are not stripped.</p> + * + * <table border="1" summary="Wrap Results"> + * <tr> + * <th>input</th> + * <th>wrapLenght</th> + * <th>newLineString</th> + * <th>wrapLongWords</th> + * <th>result</th> + * </tr> + * <tr> + * <td>null</td> + * <td>*</td> + * <td>*</td> + * <td>true/false</td> + * <td>null</td> + * </tr> + * <tr> + * <td>""</td> + * <td>*</td> + * <td>*</td> + * <td>true/false</td> + * <td>""</td> + * </tr> + * <tr> + * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> + * <td>20</td> + * <td>"\n"</td> + * <td>true/false</td> + * <td>"Here is one line of\ntext that is going\nto be wrapped after\n20 columns."</td> + * </tr> + * <tr> + * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> + * <td>20</td> + * <td>"<br />"</td> + * <td>true/false</td> + * <td>"Here is one line of<br />text that is going<br />to be wrapped after<br />20 columns."</td> + * </tr> + * <tr> + * <td>"Here is one line of text that is going to be wrapped after 20 columns."</td> + * <td>20</td> + * <td>null</td> + * <td>true/false</td> + * <td>"Here is one line of" + systemNewLine + "text that is going" + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td> + * </tr> + * <tr> + * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> + * <td>20</td> + * <td>"\n"</td> + * <td>false</td> + * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org"</td> + * </tr> + * <tr> + * <td>"Click here to jump to the commons website - http://commons.apache.org"</td> + * <td>20</td> + * <td>"\n"</td> + * <td>true</td> + * <td>"Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org"</td> + * </tr> + * </table> + * + * @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, + * <code>null</code> 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, <code>null</code> 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); http://git-wip-us.apache.org/repos/asf/commons-lang/blob/0fe9685c/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java ---------------------------------------------------------------------- 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 0da57dd..8275768 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() {