Skip to content

Commit 25f7d85

Browse files
committed
Add IterableStringTokenizer
1 parent 73c5a88 commit 25f7d85

File tree

4 files changed

+287
-7
lines changed

4 files changed

+287
-7
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ The <action> type attribute can be add,update,fix,remove.
9494
<action type="add" dev="ggregory" due-to="Gary Gregory">Add ArrayUtils.startsWith.</action>
9595
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Predicates.</action>
9696
<action type="add" dev="ggregory" due-to="Gary Gregory">Add RegExUtils methods typed to CharSequence input and deprecate old versions typed to String.</action>
97+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add IterableStringTokenizer.</action>
9798
<!-- UPDATE -->
9899
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 73 to 79 #1267, #1277, #1283, #1288, #1302.</action>
99100
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">[site] Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #1300.</action>

src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.commons.lang3.ClassUtils;
3434
import org.apache.commons.lang3.StringUtils;
3535
import org.apache.commons.lang3.reflect.MethodUtils;
36+
import org.apache.commons.lang3.util.IterableStringTokenizer;
3637

3738
/**
3839
* Provides utilities for manipulating and examining
@@ -425,13 +426,7 @@ static List<String> getStackFrameList(final Throwable throwable) {
425426
* @return an array where each element is a line from the argument
426427
*/
427428
static String[] getStackFrames(final String stackTrace) {
428-
final String linebreak = System.lineSeparator();
429-
final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
430-
final List<String> list = new ArrayList<>();
431-
while (frames.hasMoreTokens()) {
432-
list.add(frames.nextToken());
433-
}
434-
return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
429+
return new IterableStringTokenizer(stackTrace, System.lineSeparator()).toArray();
435430
}
436431

437432
/**
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
18+
package org.apache.commons.lang3.util;
19+
20+
import java.util.ArrayList;
21+
import java.util.Iterator;
22+
import java.util.List;
23+
import java.util.Objects;
24+
import java.util.StringTokenizer;
25+
import java.util.stream.Stream;
26+
import java.util.stream.StreamSupport;
27+
28+
import org.apache.commons.lang3.ArrayUtils;
29+
30+
/**
31+
* A {@link StringTokenizer} that implements {@link Iterable}{@code <String>} and conversion methods {@link #toList()} and {@link #toStream()}.
32+
*
33+
* @since 3.18.0
34+
*/
35+
public class IterableStringTokenizer extends StringTokenizer implements Iterable<String> {
36+
37+
/**
38+
* Constructs a new instance like {@link StringTokenizer#StringTokenizer(String, String, boolean)}.
39+
*
40+
* @param str a string to be parsed.
41+
* @exception NullPointerException if str is {@code null}.
42+
*/
43+
public IterableStringTokenizer(final String str) {
44+
super(str);
45+
}
46+
47+
/**
48+
* Constructs a new instance like {@link StringTokenizer#StringTokenizer(String, String, boolean)}.
49+
*
50+
* @param str a string to be parsed.
51+
* @param delim the delimiters.
52+
* @exception NullPointerException if str is {@code null}.
53+
*/
54+
public IterableStringTokenizer(final String str, final String delim) {
55+
super(str, delim);
56+
}
57+
58+
/**
59+
* Constructs a new instance like {@link StringTokenizer#StringTokenizer(String, String, boolean)}.
60+
*
61+
* @param str a string to be parsed.
62+
* @param delim the delimiters.
63+
* @param returnDelims flag indicating whether to return the delimiters as tokens.
64+
* @exception NullPointerException if str is {@code null}.
65+
*/
66+
public IterableStringTokenizer(final String str, final String delim, final boolean returnDelims) {
67+
super(str, delim, returnDelims);
68+
}
69+
70+
@Override
71+
public Iterator<String> iterator() {
72+
return new Iterator<String>() {
73+
74+
@Override
75+
public boolean hasNext() {
76+
return hasMoreElements();
77+
}
78+
79+
@Override
80+
public String next() {
81+
return Objects.toString(nextElement(), null);
82+
}
83+
};
84+
}
85+
86+
/**
87+
* Returns a new {@code String[]} containing the tokenizer elements.
88+
*
89+
* @return a new {@code String[]}.
90+
*/
91+
public String[] toArray() {
92+
return toList().toArray(ArrayUtils.EMPTY_STRING_ARRAY);
93+
}
94+
95+
/**
96+
* Returns a new {@link List} containing the tokenizer elements.
97+
*
98+
* @return a new {@link List}.
99+
*/
100+
public List<String> toList() {
101+
final List<String> list = new ArrayList<>();
102+
forEach(list::add);
103+
return list;
104+
}
105+
106+
/**
107+
* Returns a sequential stream on this Iterable instance.
108+
*
109+
* @return a sequential stream on this Iterable instance.
110+
*/
111+
public Stream<String> toStream() {
112+
return StreamSupport.stream(spliterator(), false);
113+
}
114+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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+
18+
package org.apache.commons.lang3.util;
19+
20+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.junit.jupiter.api.Assertions.assertFalse;
24+
25+
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.Iterator;
28+
import java.util.List;
29+
import java.util.StringTokenizer;
30+
import java.util.stream.Collectors;
31+
32+
import org.apache.commons.lang3.StringUtils;
33+
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.params.ParameterizedTest;
35+
import org.junit.jupiter.params.provider.MethodSource;
36+
37+
/**
38+
* Tests {@link IterableStringTokenizer}.
39+
*/
40+
public class IterableStringTokenizerTest {
41+
42+
/**
43+
* Delimiters from {@link StringTokenizer}.
44+
*/
45+
private static final String[] DELIMITERS_ARRAY = { " ", "\t", "\n", "\r", "\f" };
46+
47+
private static final String DELIMITERS_STRING = String.join("", DELIMITERS_ARRAY);
48+
49+
private static final String[] DATA = { "a", "b", "c" };
50+
51+
public static String[] delimiters() {
52+
return DELIMITERS_ARRAY;
53+
}
54+
55+
@Test
56+
public void testConstructorArguments1ForEach() {
57+
final List<String> list = new ArrayList<>();
58+
new IterableStringTokenizer("a,b,c").forEach(list::add);
59+
assertEquals(Arrays.asList("a,b,c"), list);
60+
}
61+
62+
@Test
63+
public void testConstructorArguments1ToList() {
64+
assertEquals(Arrays.asList("a,b,c"), new IterableStringTokenizer("a,b,c").toList());
65+
}
66+
67+
public void testConstructorArguments2AllDelimsToList(final String singleDelim) {
68+
final String data = String.join(singleDelim, DATA);
69+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer(data, DELIMITERS_STRING).toList());
70+
}
71+
72+
@ParameterizedTest
73+
@MethodSource("delimiters")
74+
public void testConstructorArguments2ForEach(final String singleDelim) {
75+
final List<String> list = new ArrayList<>();
76+
new IterableStringTokenizer(String.join(singleDelim, DATA), singleDelim).forEach(list::add);
77+
assertEquals(Arrays.asList(DATA), list);
78+
}
79+
80+
@ParameterizedTest
81+
@MethodSource("delimiters")
82+
public void testConstructorArguments2ToList(final String singleDelim) {
83+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer(String.join(singleDelim, DATA), singleDelim).toList());
84+
}
85+
86+
@ParameterizedTest
87+
@MethodSource("delimiters")
88+
public void testConstructorArguments3AllDelimsToList(final String singleDelim) {
89+
final String data = String.join(singleDelim, DATA);
90+
assertEquals(Arrays.asList("a", singleDelim, "b", singleDelim, "c"), new IterableStringTokenizer(data, DELIMITERS_STRING, true).toList());
91+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer(data, DELIMITERS_STRING, false).toList());
92+
}
93+
94+
@ParameterizedTest
95+
@MethodSource("delimiters")
96+
public void testConstructorArguments3ToList(final String singleDelim) {
97+
final String data = String.join(singleDelim, DATA);
98+
assertEquals(Arrays.asList("a", singleDelim, "b", singleDelim, "c"), new IterableStringTokenizer(data, singleDelim, true).toList());
99+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer(data, singleDelim, false).toList());
100+
}
101+
102+
@Test
103+
void testEmptyString() {
104+
assertTrue(new IterableStringTokenizer(StringUtils.EMPTY).toList().isEmpty());
105+
}
106+
107+
@Test
108+
void testIterator() {
109+
final IterableStringTokenizer tokenizer = new IterableStringTokenizer("a,b,c", ",");
110+
final Iterator<String> iterator = tokenizer.iterator();
111+
assertTrue(iterator.hasNext());
112+
assertEquals("a", iterator.next());
113+
assertTrue(iterator.hasNext());
114+
assertEquals("b", iterator.next());
115+
assertTrue(iterator.hasNext());
116+
assertEquals("c", iterator.next());
117+
assertFalse(iterator.hasNext());
118+
}
119+
120+
@Test
121+
void testNonDefaultDelimiterToArray() {
122+
assertArrayEquals(new String[] {}, new IterableStringTokenizer("", "|").toArray());
123+
assertArrayEquals(new String[] { "a" }, new IterableStringTokenizer("a", "|").toArray());
124+
assertArrayEquals(new String[] { "a", "b" }, new IterableStringTokenizer("a|b", "|").toArray());
125+
assertArrayEquals(new String[] { "a", "b", "c" }, new IterableStringTokenizer("a|b|c", "|").toArray());
126+
}
127+
128+
@Test
129+
void testNonDefaultDelimiterToList() {
130+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer("a|b|c", "|").toList());
131+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer("a!b!c", "!").toList());
132+
assertEquals(Arrays.asList(DATA), new IterableStringTokenizer("a^!b^!c", "^!").toList());
133+
}
134+
135+
@Test
136+
public void testToArray() {
137+
// 0 tokens
138+
assertArrayEquals(new String[] {}, new IterableStringTokenizer("").toArray());
139+
// 1 token
140+
assertArrayEquals(new String[] { "a" }, new IterableStringTokenizer("a").toArray());
141+
assertArrayEquals(new String[] { "a,b" }, new IterableStringTokenizer("a,b").toArray());
142+
assertArrayEquals(new String[] { "a,b,c" }, new IterableStringTokenizer("a,b,c").toArray());
143+
// > 1 token
144+
assertArrayEquals(new String[] { "a", "b" }, new IterableStringTokenizer("a b").toArray());
145+
}
146+
147+
@Test
148+
public void testToList() {
149+
// 0 tokens
150+
assertEquals(Arrays.asList(), new IterableStringTokenizer("").toList());
151+
// 1 token
152+
assertEquals(Arrays.asList("a"), new IterableStringTokenizer("a").toList());
153+
assertEquals(Arrays.asList("a,b"), new IterableStringTokenizer("a,b").toList());
154+
assertEquals(Arrays.asList("a,b,c"), new IterableStringTokenizer("a,b,c").toList());
155+
// > 1 token
156+
assertEquals(Arrays.asList("a", "b"), new IterableStringTokenizer("a b").toList());
157+
}
158+
159+
@Test
160+
public void testToStream() {
161+
// 0 tokens
162+
assertEquals(Arrays.asList(), new IterableStringTokenizer("").toList());
163+
// 1 token
164+
assertEquals(Arrays.asList("a"), new IterableStringTokenizer("a").toList());
165+
assertEquals(Arrays.asList("a,b"), new IterableStringTokenizer("a,b").toStream().collect(Collectors.toList()));
166+
assertEquals(Arrays.asList("a,b,c"), new IterableStringTokenizer("a,b,c").toStream().collect(Collectors.toList()));
167+
// > 1 token
168+
assertEquals(Arrays.asList("a", "b"), new IterableStringTokenizer("a b").toStream().collect(Collectors.toList()));
169+
}
170+
}

0 commit comments

Comments
 (0)