Skip to content

Commit 91e7eac

Browse files
committed
Add org.apache.commons.text.RandomStringGenerator.Builder.setAccumulate(boolean)
This is instead of breaking compatibility with #125
1 parent 73c17bc commit 91e7eac

File tree

3 files changed

+100
-18
lines changed

3 files changed

+100
-18
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ The <action> type attribute can be add,update,fix,remove.
6161
<action type="add" dev="ggregory" due-to="Gary Gregory">Interface TextRandomProvider extends IntUnaryOperator.</action>
6262
<action type="add" dev="ggregory" due-to="Gary Gregory">Add RandomStringGenerator.Builder.usingRandom(IntUnaryOperator).</action>
6363
<action type="add" dev="ggregory" due-to="Gary Gregory">Add PMD check to default Maven goal.</action>
64+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.text.RandomStringGenerator.Builder.setAccumulate(boolean).</action>
6465
<!-- UPDATE -->
6566
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump org.apache.commons:commons-parent from 81 to 85 #668.</action>
6667
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-io:commons-io from 2.18.0 to 2.20.0.</action>

src/main/java/org/apache/commons/text/RandomStringGenerator.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,12 @@ public static class Builder implements org.apache.commons.text.Builder<RandomStr
125125
/**
126126
* The source of provided characters.
127127
*/
128-
private List<Character> characterList;
128+
private Set<Character> characterSet = new HashSet<>();
129+
130+
/**
131+
* Whether calls accumulates the source of provided characters. The default is {@code false}.
132+
*/
133+
private boolean accumulate;
129134

130135
/**
131136
* Creates a new instance.
@@ -155,7 +160,7 @@ public RandomStringGenerator build() {
155160
* </p>
156161
*
157162
* @param predicates the predicates, may be {@code null} or empty.
158-
* @return {@code this}, to allow method chaining.
163+
* @return {@code this} instance.
159164
*/
160165
public Builder filteredBy(final CharacterPredicate... predicates) {
161166
if (ArrayUtils.isEmpty(predicates)) {
@@ -182,6 +187,12 @@ public RandomStringGenerator get() {
182187
return new RandomStringGenerator(this);
183188
}
184189

190+
private void initCharList() {
191+
if (!accumulate) {
192+
characterSet = new HashSet<>();
193+
}
194+
}
195+
185196
/**
186197
* Limits the characters in the generated string to those who match at supplied list of Character.
187198
*
@@ -191,19 +202,43 @@ public RandomStringGenerator get() {
191202
* </p>
192203
*
193204
* @param chars set of predefined Characters for random string generation the Character can be, may be {@code null} or empty
194-
* @return {@code this}, to allow method chaining.
205+
* @return {@code this} instance.
195206
* @since 1.2
196207
*/
197208
public Builder selectFrom(final char... chars) {
198-
characterList = new ArrayList<>();
209+
initCharList();
199210
if (chars != null) {
200211
for (final char c : chars) {
201-
characterList.add(c);
212+
characterSet.add(c);
202213
}
203214
}
204215
return this;
205216
}
206217

218+
/**
219+
* Sets whether calls accumulates the source of provided characters. The default is {@code false}.
220+
*
221+
* <pre>
222+
* {@code
223+
* RandomStringGenerator gen = RandomStringGenerator.builder()
224+
* .setAccumulate(true)
225+
* .withinRange(new char[][] { { 'a', 'z' }, { 'A', 'Z' }, { '0', '9' } })
226+
* .selectFrom('!', '"', '#', '$', '&', '\'', '(', ')', ',', '.', ':', ';', '?', '@', '[',
227+
* '\\', ']', '^', '_', '`', '{', '|', '}', '~') // punctuation
228+
* // additional builder calls as needed
229+
* .build();
230+
* }
231+
* </pre>
232+
*
233+
* @param accumulate whether calls accumulates the source of provided characters. The default is {@code false}.
234+
* @return {@code this} instance.
235+
* @since 1.14.0
236+
*/
237+
public Builder setAccumulate(final boolean accumulate) {
238+
this.accumulate = accumulate;
239+
return this;
240+
}
241+
207242
/**
208243
* Overrides the default source of randomness. It is highly recommended that a random number generator library like
209244
* <a href="https://commons.apache.org/proper/commons-rng/">Apache Commons RNG</a> be used to provide the random number generation.
@@ -227,7 +262,7 @@ public Builder selectFrom(final char... chars) {
227262
* </p>
228263
*
229264
* @param random the source of randomness, may be {@code null}.
230-
* @return {@code this}, to allow method chaining.
265+
* @return {@code this} instance.
231266
* @since 1.14.0
232267
*/
233268
public Builder usingRandom(final IntUnaryOperator random) {
@@ -258,7 +293,7 @@ public Builder usingRandom(final IntUnaryOperator random) {
258293
* </p>
259294
*
260295
* @param random the source of randomness, may be {@code null}.
261-
* @return {@code this}, to allow method chaining.
296+
* @return {@code this} instance.
262297
*/
263298
public Builder usingRandom(final TextRandomProvider random) {
264299
this.random = random;
@@ -272,18 +307,17 @@ public Builder usingRandom(final TextRandomProvider random) {
272307
*
273308
* <pre>
274309
* {@code
275-
*
276310
* char[][] pairs = { { '0', '9' } };
277311
* char[][] pairs = { { 'a', 'z' } };
278312
* char[][] pairs = { { 'a', 'z' }, { '0', '9' } };
279313
* }
280314
* </pre>
281315
*
282316
* @param pairs array of characters array, expected is to pass min, max pairs through this arg.
283-
* @return {@code this}, to allow method chaining.
317+
* @return {@code this} instance.
284318
*/
285319
public Builder withinRange(final char[]... pairs) {
286-
characterList = new ArrayList<>();
320+
initCharList();
287321
if (pairs != null) {
288322
for (final char[] pair : pairs) {
289323
Validate.isTrue(pair.length == 2, "Each pair must contain minimum and maximum code point");
@@ -292,19 +326,20 @@ public Builder withinRange(final char[]... pairs) {
292326
Validate.isTrue(minimumCodePoint <= maximumCodePoint, "Minimum code point %d is larger than maximum code point %d", minimumCodePoint,
293327
maximumCodePoint);
294328
for (int index = minimumCodePoint; index <= maximumCodePoint; index++) {
295-
characterList.add((char) index);
329+
characterSet.add((char) index);
296330
}
297331
}
298332
}
299333
return this;
300334
}
301335

336+
302337
/**
303338
* Sets the minimum and maximum code points allowed in the generated string.
304339
*
305340
* @param minimumCodePoint the smallest code point allowed (inclusive).
306341
* @param maximumCodePoint the largest code point allowed (inclusive).
307-
* @return {@code this}, to allow method chaining.
342+
* @return {@code this} instance.
308343
* @throws IllegalArgumentException if {@code maximumCodePoint >} {@link Character#MAX_CODE_POINT}.
309344
* @throws IllegalArgumentException if {@code minimumCodePoint < 0}.
310345
* @throws IllegalArgumentException if {@code minimumCodePoint > maximumCodePoint}.
@@ -362,14 +397,14 @@ public static Builder builder() {
362397
* @param maximumCodePoint largest allowed code point (inclusive).
363398
* @param inclusivePredicates filters for code points.
364399
* @param random source of randomness.
365-
* @param characterList list of predefined set of characters.
400+
* @param characterSet list of predefined set of characters.
366401
*/
367402
private RandomStringGenerator(final Builder builder) {
368403
this.minimumCodePoint = builder.minimumCodePoint;
369404
this.maximumCodePoint = builder.maximumCodePoint;
370405
this.inclusivePredicates = builder.inclusivePredicates;
371406
this.random = builder.random;
372-
this.characterList = builder.characterList;
407+
this.characterList = new ArrayList<>(builder.characterSet);
373408
}
374409

375410
/**

src/test/java/org/apache/commons/text/RandomStringGeneratorTest.java

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@
2323
import static org.junit.jupiter.api.Assertions.assertTrue;
2424
import static org.junit.jupiter.api.Assertions.fail;
2525

26+
import java.util.Arrays;
2627
import java.util.function.IntUnaryOperator;
2728

29+
import org.apache.commons.lang3.ArraySorter;
2830
import org.apache.commons.text.RandomStringGenerator.Builder;
2931
import org.junit.jupiter.api.Test;
32+
import org.junit.jupiter.params.ParameterizedTest;
33+
import org.junit.jupiter.params.provider.ValueSource;
3034

3135
/**
32-
* Tests for {@link RandomStringGenerator}
36+
* Tests for {@link RandomStringGenerator}.
3337
*/
3438
class RandomStringGeneratorTest {
3539

@@ -159,6 +163,25 @@ void testNoPrivateCharacters() {
159163
} while (i < str.length());
160164
}
161165

166+
@Test
167+
void testPasswordExample() {
168+
final char[] punctuation = ArraySorter
169+
.sort(new char[] { '!', '"', '#', '$', '&', '\'', '(', ')', ',', '.', ':', ';', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' });
170+
// @formatter:off
171+
final RandomStringGenerator generator = RandomStringGenerator.builder()
172+
.setAccumulate(true)
173+
.withinRange('a', 'z')
174+
.withinRange('A', 'Z')
175+
.withinRange('0', '9')
176+
.selectFrom(punctuation)
177+
.get();
178+
// @formatter:on
179+
final String randomText = generator.generate(10);
180+
for (final char c : randomText.toCharArray()) {
181+
assertTrue(Character.isLetter(c) || Character.isDigit(c) || Arrays.binarySearch(punctuation, c) >= 0);
182+
}
183+
}
184+
162185
@Test
163186
void testRemoveFilters() {
164187
final RandomStringGenerator.Builder builder = RandomStringGenerator.builder().withinRange('a', 'z').filteredBy(A_FILTER);
@@ -194,17 +217,19 @@ void testSelectFromCharVarargs() {
194217
}
195218
}
196219

197-
@Test
198-
void testSelectFromCharVarargs2() {
220+
@ParameterizedTest
221+
@ValueSource(booleans = {false, true})
222+
void testSelectFromCharVarargs2(final boolean accumulate) {
199223
final String str = "abcde";
200224
// @formatter:off
201225
final RandomStringGenerator generator = RandomStringGenerator.builder()
226+
.setAccumulate(accumulate)
202227
.selectFrom()
203228
.selectFrom(null)
204229
.selectFrom('a', 'b')
205230
.selectFrom('a', 'b', 'c')
206231
.selectFrom('a', 'b', 'c', 'd')
207-
.selectFrom('a', 'b', 'c', 'd', 'e') // only this last call matters
232+
.selectFrom('a', 'b', 'c', 'd', 'e') // only this last call matters when accumulate is false
208233
.build();
209234
// @formatter:on
210235
final String randomText = generator.generate(10);
@@ -213,6 +238,27 @@ void testSelectFromCharVarargs2() {
213238
}
214239
}
215240

241+
@ParameterizedTest
242+
@ValueSource(booleans = {false, true})
243+
void testSelectFromCharVarargs3(final boolean accumulate) {
244+
final String str = "abcde";
245+
// @formatter:off
246+
final RandomStringGenerator generator = RandomStringGenerator.builder()
247+
.setAccumulate(accumulate)
248+
.selectFrom('a', 'b', 'c', 'd', 'e')
249+
.selectFrom('a', 'b', 'c', 'd')
250+
.selectFrom('a', 'b', 'c')
251+
.selectFrom('a', 'b')
252+
.selectFrom(null)
253+
.selectFrom()
254+
.get();
255+
// @formatter:on
256+
final String randomText = generator.generate(10);
257+
for (final char c : randomText.toCharArray()) {
258+
assertEquals(accumulate, str.indexOf(c) != -1);
259+
}
260+
}
261+
216262
@Test
217263
void testSelectFromCharVarargSize1() {
218264
final RandomStringGenerator generator = RandomStringGenerator.builder().selectFrom('a').build();

0 commit comments

Comments
 (0)