Skip to content

Add string utils repeat chars x times #1401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_17.</action>
<action issue="LANG-1636" type="add" dev="ggregory" due-to="">Add missing boolean[] join method #686.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add StringUtils.substringBefore(String, int).</action>
<action type="add" dev="trungpnt" due-to="Trung Pham">Add StringUtils.resizeConsecutiveChars(String, char, int, boolean, boolean).</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Range.INTEGER.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add DurationUtils.</action>
<action type="add" dev="jochen">Introduce the use of @Nonnull, and @Nullable, and the Objects class as a helper tool.</action>
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/apache/commons/lang3/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9287,6 +9287,44 @@ public static String wrapIfMissing(final String str, final String wrapWith) {
return builder.toString();
}

public static String repeatChars(String str, Set<Character> appointedChars, Integer repetitions, boolean isResizeOnlyConsecutive) {
if (str == null || str.isEmpty()) {
return str;
}
final int xTimes = repetitions != null ? repetitions : 1;
final int len = str.length();
final int nbrOfCharsToResize = appointedChars.size();
final int newOutputMaxPossibleLen = (nbrOfCharsToResize * xTimes) + len;
final char[] collector = new char[newOutputMaxPossibleLen];
int collectorIdx = 0;
for (int i = 0; i < len; i++) {
final char current = str.charAt(i);
if (appointedChars.contains(current)) {
int spanEnd = i + 1;
while (spanEnd < str.length() && current == str.charAt(spanEnd)) {
spanEnd++;
}
if (spanEnd == i + 1) {
collector[collectorIdx++] = current;
} else if (!isResizeOnlyConsecutive || spanEnd > i + 1) {
int repetition = xTimes;
while (repetition-- > 0) {
collector[collectorIdx++] = current;
}
}
i = spanEnd - 1;
} else {
collector[collectorIdx++] = current;
}
}
return new String(collector, 0, collectorIdx);
}

public static String ensureOneWhitespace(String str) {
return repeatChars(str, new HashSet<>(Collections.singletonList(' ')), 1, true);
}


/**
* {@link StringUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used as
Expand Down
44 changes: 43 additions & 1 deletion src/test/java/org/apache/commons/lang3/StringUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -3246,4 +3247,45 @@ public void testWrapIfMissing_StringString() {
assertSame("ab/ab", StringUtils.wrapIfMissing("ab/ab", "ab"));
assertSame("//x//", StringUtils.wrapIfMissing("//x//", "//"));
}

@Test
public void repeatChars() {
/**
* Resize 1 consecutive characters in a string to a specified length.
*/
assertEquals("1 1 1 1",StringUtils.repeatChars("11111 111111 111111 111111",new HashSet<>(Collections.singletonList('1')),1,true));
assertEquals("aabbbcc",StringUtils.repeatChars("aaabbbcc",new HashSet<>(Collections.singletonList('a')),2,true));
assertEquals("1",StringUtils.repeatChars("111111",new HashSet<>(Collections.singletonList('1')),null,true));
assertEquals(".$$$42.1424.1.2.5.12.$!@.$!@%$.",StringUtils.repeatChars(".....$$$42....1424.1.....2.5.12..$!@..$!@%$...",new HashSet<>(Arrays.asList('.')),1,true));
assertEquals(".$$$42.1424.1.2.5.12.$!@.$!@%$.",StringUtils.repeatChars(".....$$$42....1424.1.....2.5.12..$!@..$!@%$...",new HashSet<>(Arrays.asList('.')),1,false));
assertEquals("1ewjaoij1 a1w1e1o1iw1 1213121 1",StringUtils.repeatChars("1ewjaoij1 a11w11111e11o11iw11 12113121 1",new HashSet<>(Arrays.asList('1')),1,true));
/**
* Resize to 2 repetitions with multiple consecutive characters.
*/
assertEquals("11 11 11 11",StringUtils.repeatChars("11111 111111 111111 111111",new HashSet<>(Collections.singletonList('1')),2,true));
assertEquals("11 22 33 44",StringUtils.repeatChars("11111 222222 333333 444",new HashSet<>(Arrays.asList('1','2','3','4')),2,true));
assertEquals("11 22342422 33 44",StringUtils.repeatChars("11111 223424222 333333 444",new HashSet<>(Arrays.asList('1','2','3','4')),2,true));
assertEquals("1234",StringUtils.repeatChars("1234",new HashSet<>(Arrays.asList('1','2','3','4')),2,true));
assertEquals("11234",StringUtils.repeatChars("111111234",new HashSet<>(Arrays.asList('1','2','3','4')),2,true));
assertEquals("23411111",StringUtils.repeatChars("23411",new HashSet<>(Arrays.asList('1','2','3','4')),5,true));

// resizes whitespaces trimming
assertNull(StringUtils.ensureOneWhitespace(null));
assertEquals("",StringUtils.ensureOneWhitespace(""));
assertEquals(" ",StringUtils.ensureOneWhitespace(" "));
assertEquals("Hello",StringUtils.ensureOneWhitespace("Hello"));
assertEquals("Hello,World!",StringUtils.ensureOneWhitespace("Hello,World!"));
assertEquals("Hello,World!",StringUtils.ensureOneWhitespace("Hello, World!"));
assertEquals("a a ",StringUtils.ensureOneWhitespace("a a "));
assertEquals(" a c b ncw a c j j j j j",StringUtils.ensureOneWhitespace(" a c b ncw a c j j j j j"));
// resizes whitespaces without trimming
assertNull(StringUtils.ensureOneWhitespace(null));
assertEquals("",StringUtils.ensureOneWhitespace(""));
assertEquals(" ",StringUtils.ensureOneWhitespace(" "));
assertEquals("Hello",StringUtils.ensureOneWhitespace("Hello"));
assertEquals(" a b c ",StringUtils.ensureOneWhitespace(" a b c "));
assertEquals(" Hello,World! ",StringUtils.ensureOneWhitespace(" Hello, World! "));
assertEquals("Hello,World!",StringUtils.ensureOneWhitespace("Hello, World!"));
assertEquals(" a a ",StringUtils.ensureOneWhitespace(" a a "));
}
}