> On 27 Dec 2016, at 08:45, djo...@apache.org wrote: > > Repository: commons-text > Updated Branches: > refs/heads/master 6f6da3467 -> 08ac56a50 > > > Rename RandomStringBuilder and make it immutable and thread-safe. > > The RandomStringBuilder class was renamed to RandomStringGenerator. The > class is now constructed using an inner Builder class, resulting in an > immutable and thread-safe generator. The default random generator is now > ThreadLocalRandom rather than Random. > > The inner CodePointPredicate class was extracted into a separate class > and renamed to CharacterPredicate. The commonly used predicates are now > in a separate enum, CharacterPredicates. > > Project: http://git-wip-us.apache.org/repos/asf/commons-text/repo > Commit: http://git-wip-us.apache.org/repos/asf/commons-text/commit/08ac56a5 > Tree: http://git-wip-us.apache.org/repos/asf/commons-text/tree/08ac56a5 > Diff: http://git-wip-us.apache.org/repos/asf/commons-text/diff/08ac56a5 > > Branch: refs/heads/master > Commit: 08ac56a502adb0d274d02f9d97f394e4a5c5966e > Parents: 6f6da34 > Author: duncan <dun...@wortharead.com> > Authored: Tue Dec 27 08:18:52 2016 +0000 > Committer: duncan <dun...@wortharead.com> > Committed: Tue Dec 27 08:42:48 2016 +0000 > > ---------------------------------------------------------------------- > .../apache/commons/text/CharacterPredicate.java | 37 ++ > .../commons/text/CharacterPredicates.java | 52 +++ > .../commons/text/RandomStringBuilder.java | 354 ------------------- > .../commons/text/RandomStringGenerator.java | 316 +++++++++++++++++ > .../commons/text/CharacterPredicatesTest.java | 50 +++ > .../commons/text/RandomStringBuilderTest.java | 235 ------------ > .../commons/text/RandomStringGeneratorTest.java | 206 +++++++++++ > 7 files changed, 661 insertions(+), 589 deletions(-) > ———————————————————————————————————
Apologies, should have split this into multiple commits but I got too far into it before I reached that conclusion! > > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/main/java/org/apache/commons/text/CharacterPredicate.java > ---------------------------------------------------------------------- > diff --git a/src/main/java/org/apache/commons/text/CharacterPredicate.java > b/src/main/java/org/apache/commons/text/CharacterPredicate.java > new file mode 100644 > index 0000000..fb23ac4 > --- /dev/null > +++ b/src/main/java/org/apache/commons/text/CharacterPredicate.java > @@ -0,0 +1,37 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache License, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > +package org.apache.commons.text; > + > +/** > + * A predicate for selecting code points. Implementations of this interface > must > + * be thread safe. > + * > + * @since 1.0 > + */ > +public interface CharacterPredicate { > + > + /** > + * Tests the code point with this predicate. > + * > + * @param codePoint > + * the code point to test > + * @return {@code true} if the code point matches the predicate, > + * {@code false} otherwise > + * @since 1.0 > + */ > + boolean test(int codePoint); > +} > \ No newline at end of file > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/main/java/org/apache/commons/text/CharacterPredicates.java > ---------------------------------------------------------------------- > diff --git a/src/main/java/org/apache/commons/text/CharacterPredicates.java > b/src/main/java/org/apache/commons/text/CharacterPredicates.java > new file mode 100644 > index 0000000..7f38552 > --- /dev/null > +++ b/src/main/java/org/apache/commons/text/CharacterPredicates.java > @@ -0,0 +1,52 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache License, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > +package org.apache.commons.text; > + > +/** > + * <p> > + * Commonly used implementations of {@link CharacterPredicate}. Per the > interface > + * requirements, all implementations are thread safe. > + * </p> > + * > + * @since 1.0 > + */ > +public enum CharacterPredicates implements CharacterPredicate { > + > + /** > + * Tests code points against {@link Character#isLetter(int)} > + * > + * @since 1.0 > + */ > + LETTERS { > + @Override > + public boolean test(int codePoint) { > + return Character.isLetter(codePoint); > + } > + }, > + > + /** > + * Tests code points against {@link Character#isDigit(int)}. > + * > + * @since 1.0 > + */ > + DIGITS { > + @Override > + public boolean test(int codePoint) { > + return Character.isDigit(codePoint); > + } > + } > +} > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/main/java/org/apache/commons/text/RandomStringBuilder.java > ---------------------------------------------------------------------- > diff --git a/src/main/java/org/apache/commons/text/RandomStringBuilder.java > b/src/main/java/org/apache/commons/text/RandomStringBuilder.java > deleted file mode 100644 > index 38ba974..0000000 > --- a/src/main/java/org/apache/commons/text/RandomStringBuilder.java > +++ /dev/null > @@ -1,354 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one or more > - * contributor license agreements. See the NOTICE file distributed with > - * this work for additional information regarding copyright ownership. > - * The ASF licenses this file to You under the Apache License, Version 2.0 > - * (the "License"); you may not use this file except in compliance with > - * the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, software > - * distributed under the License is distributed on an "AS IS" BASIS, > - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > - * See the License for the specific language governing permissions and > - * limitations under the License. > - */ > -package org.apache.commons.text; > - > -import java.util.HashSet; > -import java.util.Random; > -import java.util.Set; > - > -/** > - * <p> > - * Generates a random Unicode string based on properties defined using a > builder > - * pattern. > - * </p> > - * <p> > - * Overriding the default properties is optional, however callers will need > to > - * define the length of the output string using {@link #ofLength(int)} to > avoid > - * generating an empty string. > - * </p> > - * <p> > - * All the property setting methods return the {@code RandomStringBuilder} > - * instance to allow for method chaining: > - * </p> > - * > - * <pre> > - * // Generates a 20 code point string, using only the letters a-z > - * String random = new > RandomStringBuilder().ofLength(20).withinRange('a','z').build(); > - * </pre> > - * > - * <p> > - * The type of code point returned can be filtered using > - * {@link #filteredBy(CodePointPredicate...)}, which defines a collection of > - * tests that are applied to the randomly generated code points. The code > points > - * will only be included in the result if they pass at least one of the > tests. > - * Some commonly used predicates are provided (e.g. {@link #LETTERS} or > - * {@link #DIGITS}) and others can be created by implementing > - * {@link CodePointPredicate}. > - * </p> > - * > - * <pre> > - * // Generates a 10 code point string containing only letters > - * > - * import static org.apache.commons.text.RandomStringBuilder.LETTERS; > - * ... > - * String random = new > RandomStringBuilder().ofLength(10).filteredBy(LETTERS).build(); > - * </pre> > - * > - * <p> > - * A {@code RandomStringBuilder} instance can be used multiple times to > generate > - * different random strings, however it cannot safely be shared between > threads. > - * </p> > - * > - * @since 1.0 > - */ > -public class RandomStringBuilder implements Builder<String> { > - > - /** > - * Default source of randomness > - */ > - private static final Random DEFAULT_RANDOM = new Random(); > - > - /** > - * The default string length produced by this builder: {@value} > - * > - * @since 1.0 > - */ > - public static final int DEFAULT_LENGTH = 0; > - > - /** > - * The default minimum code point allowed: {@value} > - * > - * @since 1.0 > - */ > - public static final int DEFAULT_MINIMUM_CODE_POINT = 0; > - > - /** > - * The default maximum code point allowed: {@link > Character#MAX_CODE_POINT} > - * ({@value}) > - * > - * @since 1.0 > - */ > - public static final int DEFAULT_MAXIMUM_CODE_POINT = > Character.MAX_CODE_POINT; > - > - private int length = 0; > - private int minimumCodePoint = 0; > - private int maximumCodePoint = Character.MAX_CODE_POINT; > - private Set<CodePointPredicate> inclusivePredicates = null; > - private Random random = null; > - > - /** > - * <p> > - * Constructs a builder with default properties: > - * </p> > - * > - * <ul> > - * <li>Length: {@value #DEFAULT_LENGTH}</li> > - * <li>Minimum code point: {@value #DEFAULT_MINIMUM_CODE_POINT}</li> > - * <li>Maximum code point: {@link Character#MAX_CODE_POINT}</li> > - * <li>Default source of randomness</li> > - * <li>No character filters</li> > - * </ul> > - * > - * @since 1.0 > - */ > - public RandomStringBuilder() { > - } > - > - /** > - * <p> > - * Specifies how many code points to generate in the random string. > - * </p> > - * <p> > - * Note: the number of {@code char} code units generated will exceed > - * {@code length} if the string contains supplementary characters. See > the > - * {@link Character} documentation to understand how Java stores Unicode > - * values. > - * </p> > - * > - * @param length > - * the number of code points to generate > - * @return {@code this}, to allow method chaining > - * @throws IllegalArgumentException > - * if {@code length < 0} > - * @since 1.0 > - */ > - public RandomStringBuilder ofLength(final int length) { > - if (length < 0) { > - throw new IllegalArgumentException(String.format("Length %d is > smaller than zero.", length)); > - } > - > - this.length = length; > - return this; > - } > - > - /** > - * <p> > - * Specifies the minimum and maximum code points allowed in the generated > - * string. > - * </p> > - * > - * @param minimumCodePoint > - * the smallest code point allowed (inclusive) > - * @param maximumCodePoint > - * the largest code point allowed (inclusive) > - * @return {@code this}, to allow method chaining > - * @throws IllegalArgumentException > - * if {@code maximumCodePoint >} > - * {@link Character#MAX_CODE_POINT} > - * @throws IllegalArgumentException > - * if {@code minimumCodePoint < 0} > - * @throws IllegalArgumentException > - * if {@code minimumCodePoint > maximumCodePoint} > - * @since 1.0 > - */ > - public RandomStringBuilder withinRange(final int minimumCodePoint, final > int maximumCodePoint) { > - if (minimumCodePoint > maximumCodePoint) { > - throw new IllegalArgumentException(String.format( > - "Minimum code point %d is larger than maximum code point > %d", minimumCodePoint, maximumCodePoint)); > - } > - if (minimumCodePoint < 0) { > - throw new IllegalArgumentException(String.format("Minimum code > point %d is negative", minimumCodePoint)); > - } > - if (maximumCodePoint > Character.MAX_CODE_POINT) { > - throw new IllegalArgumentException( > - String.format("Value %d is larger than > Character.MAX_CODE_POINT.", maximumCodePoint)); > - } > - > - this.minimumCodePoint = minimumCodePoint; > - this.maximumCodePoint = maximumCodePoint; > - return this; > - } > - > - /** > - * <p> > - * Overrides the default source of randomness. > - * </p> > - * > - * <p> > - * Passing {@code null} to this method will revert to the default source > of > - * randomness. > - * </p> > - * > - * @param random > - * the source of randomness, may be {@code null} > - * @return {@code this}, to allow method chaining > - * @since 1.0 > - */ > - public RandomStringBuilder usingRandom(final Random random) { > - this.random = random; > - return this; > - } > - > - /** > - * <p> > - * Limits the characters in the generated string to those that match at > - * least one of the predicates supplied. > - * </p> > - * > - * <p> > - * Passing {@code null} or an empty array to this method will revert to > the > - * default behaviour of allowing any character. Multiple calls to this > - * method will replace the previously stored predicates. > - * </p> > - * > - * @param predicates > - * the predicates, may be {@code null} or empty > - * @return {@code this}, to allow method chaining > - * @since 1.0 > - */ > - public RandomStringBuilder filteredBy(final CodePointPredicate... > predicates) { > - if (predicates == null || predicates.length == 0) { > - inclusivePredicates = null; > - return this; > - } > - > - if (inclusivePredicates == null) { > - inclusivePredicates = new HashSet<>(); > - } else { > - inclusivePredicates.clear(); > - } > - > - for (CodePointPredicate predicate : predicates) { > - inclusivePredicates.add(predicate); > - } > - > - return this; > - } > - > - /** > - * <p> > - * Generates a random string using the settings defined in this builder. > - * Code points are randomly selected between the minimum and maximum > values. > - * Surrogate and private use characters are not returned, although the > - * resulting string may contain pairs of surrogates that together encode > a > - * supplementary character. > - * </p> > - * > - * <p> > - * A static {@code Random} instance is used if an alternative wasn't > - * provided via {@link #usingRandom(Random)}. > - * </p> > - * > - * @return the randomly generated string > - * @since 1.0 > - */ > - @Override > - public String build() { > - if (length == 0) { > - return ""; > - } > - > - if (random == null) { > - random = DEFAULT_RANDOM; > - } > - > - final StringBuilder builder = new StringBuilder(length); > - long remaining = length; > - > - do { > - int codePoint = random.nextInt(maximumCodePoint - > minimumCodePoint + 1) + minimumCodePoint; > - > - switch (Character.getType(codePoint)) { > - case Character.UNASSIGNED: > - case Character.PRIVATE_USE: > - case Character.SURROGATE: > - continue; > - } > - > - if (inclusivePredicates != null) { > - boolean matchedFilter = false; > - for (CodePointPredicate predicate : inclusivePredicates) { > - if (predicate.test(codePoint)) { > - matchedFilter = true; > - break; > - } > - } > - if (!matchedFilter) { > - continue; > - } > - } > - > - builder.appendCodePoint(codePoint); > - remaining--; > - > - } while (remaining != 0); > - > - return builder.toString(); > - } > - > - /** > - * A predicate for selecting code points. > - * > - * @since 1.0 > - */ > - public static interface CodePointPredicate { > - /** > - * Tests the code point with this predicate. > - * > - * @param codePoint > - * the code point to test > - * @return {@code true} if the code point matches the predicate, > - * {@code false} otherwise > - * @since 1.0 > - */ > - boolean test(int codePoint); > - } > - > - /** > - * Tests code points against {@link Character#isLetter(int)}. > - * > - * @since 1.0 > - */ > - public static final CodePointPredicate LETTERS = new LetterPredicate(); > - > - /** > - * Tests code points against {@link Character#isDigit(int)}. > - * > - * @since 1.0 > - */ > - public static final CodePointPredicate DIGITS = new DigitPredicate(); > - > - /** > - * Tests whether code points are letters. > - */ > - private static final class LetterPredicate implements CodePointPredicate > { > - @Override > - public boolean test(int codePoint) { > - return Character.isLetter(codePoint); > - } > - } > - > - /** > - * Tests whether code points are digits. > - */ > - private static final class DigitPredicate implements CodePointPredicate { > - @Override > - public boolean test(int codePoint) { > - return Character.isDigit(codePoint); > - } > - }; > -} > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/main/java/org/apache/commons/text/RandomStringGenerator.java > ---------------------------------------------------------------------- > diff --git a/src/main/java/org/apache/commons/text/RandomStringGenerator.java > b/src/main/java/org/apache/commons/text/RandomStringGenerator.java > new file mode 100644 > index 0000000..c4411a2 > --- /dev/null > +++ b/src/main/java/org/apache/commons/text/RandomStringGenerator.java > @@ -0,0 +1,316 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache License, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > +package org.apache.commons.text; > + > +import java.util.HashSet; > +import java.util.Random; > +import java.util.Set; > +import java.util.concurrent.ThreadLocalRandom; > + > +/** > + * <p> > + * Generates random Unicode strings containing the specified number of code > points. > + * Instances are created using a builder class, which allows the > + * callers to define the properties of the generator. See the documentation > for the > + * {@link Builder} class to see available properties. > + * </p> > + * > + * <pre> > + * // Generates a 20 code point string, using only the letters a-z > + * RandomStringGenerator generator = new > RandomStringGenerator.Builder().withinRange('a', 'z').build(); > + * String random = generator.generate(20); > + * </pre> > + * > + * <p> > + * {@code RandomStringBuilder} instances are immutable and thread-safe. > + * </p> > + * > + * @since 1.0 > + */ > +public final class RandomStringGenerator { > + > + private final int minimumCodePoint; > + private final int maximumCodePoint; > + private final Set<CharacterPredicate> inclusivePredicates; > + private final Random random; > + > + > + /** > + * Constructs the generator. > + * > + * @param minimumCodePoint > + * smallest allowed code point (inclusive) > + * @param maximumCodePoint > + * largest allowed code point (inclusive) > + * @param inclusivePredicates > + * filters for code points > + * @param random > + * source of randomness > + */ > + private RandomStringGenerator(int minimumCodePoint, int maximumCodePoint, > + Set<CharacterPredicate> inclusivePredicates, Random random) { > + this.minimumCodePoint = minimumCodePoint; > + this.maximumCodePoint = maximumCodePoint; > + this.inclusivePredicates = inclusivePredicates; > + this.random = random; > + } > + > + /** > + * Generates a random number within a range, using a > + * {@link ThreadLocalRandom} instance or the user-supplied source of > + * randomness. > + * > + * @param minInclusive > + * the minimum value allowed > + * @param maxInclusive > + * the maximum value allowed > + * @return the random number. > + */ > + private int generateRandomNumber(final int minInclusive, final int > maxInclusive) { > + if (random != null) { > + return random.nextInt(maxInclusive - minInclusive + 1) + > minInclusive; > + } > + > + return ThreadLocalRandom.current().nextInt(minInclusive, > maxInclusive + 1); > + } > + > + > + /** > + * <p> > + * Generates a random string, containing the specified number of code > points. > + * </p> > + * <p>Code points are randomly selected between the minimum and maximum > values defined > + * in the generator. > + * Surrogate and private use characters are not returned, although the > + * resulting string may contain pairs of surrogates that together encode > a > + * supplementary character. > + * </p> > + * <p> > + * Note: the number of {@code char} code units generated will exceed > + * {@code length} if the string contains supplementary characters. See > the > + * {@link Character} documentation to understand how Java stores Unicode > + * values. > + * </p> > + * > + * @param length > + * the number of code points to generate > + * @return the generated string > + * @throws IllegalArgumentException > + * if {@code length < 0} > + * @since 1.0 > + */ > + public String generate(final int length) { > + if (length == 0) { > + return ""; > + } > + > + if (length < 0) { > + throw new IllegalArgumentException(String.format("Length %d is > smaller than zero.", length)); > + } > + > + final StringBuilder builder = new StringBuilder(length); > + long remaining = length; > + > + do { > + int codePoint = generateRandomNumber(minimumCodePoint, > maximumCodePoint); > + > + switch (Character.getType(codePoint)) { > + case Character.UNASSIGNED: > + case Character.PRIVATE_USE: > + case Character.SURROGATE: > + continue; > + } > + > + if (inclusivePredicates != null) { > + boolean matchedFilter = false; > + for (CharacterPredicate predicate : inclusivePredicates) { > + if (predicate.test(codePoint)) { > + matchedFilter = true; > + break; > + } > + } > + if (!matchedFilter) { > + continue; > + } > + } > + > + builder.appendCodePoint(codePoint); > + remaining--; > + > + } while (remaining != 0); > + > + return builder.toString(); > + } > + > + > + /** > + * <p>A builder for generating {@code RandomStringGenerator} > instances.</p> > + * <p>The behaviour of a generator is controlled by properties set by > this > + * builder. Each property has a default value, which can be overridden by > + * calling the methods defined in this class, prior to calling {@link > #build()}.</p> > + * > + * <p>All the property setting methods return the {@code Builder} > instance to allow for method chaining.</p> > + * > + * <p>The minimum and maximum code point values are defined using {@link > #withinRange(int, int)}. The > + * default values are {@code 0} and {@link Character#MAX_CODE_POINT} > respectively.</p> > + * > + * <p>The source of randomness can be set using {@link > #usingRandom(Random)}, otherwise {@link ThreadLocalRandom} > + * is used.</p> > + * > + * <p>The type of code points returned can be filtered using {@link > #filteredBy(CharacterPredicate...)}, > + * which defines a collection of > + * tests that are applied to the randomly generated code points. The > code points > + * will only be included in the result if they pass at least one of the > tests. > + * Some commonly used predicates are provided by the {@link > CharacterPredicates} enum.</p> > + * > + * <p>This class is not thread safe.</p> > + * @since 1.0 > + */ > + public static class Builder implements > org.apache.commons.text.Builder<RandomStringGenerator> { > + > + /** > + * The default maximum code point allowed: {@link > Character#MAX_CODE_POINT} > + * ({@value}) > + * > + * @since 1.0 > + */ > + public static final int DEFAULT_MAXIMUM_CODE_POINT = > Character.MAX_CODE_POINT; > + > + /** > + * The default string length produced by this builder: {@value} > + * > + * @since 1.0 > + */ > + public static final int DEFAULT_LENGTH = 0; > + > + /** > + * The default minimum code point allowed: {@value} > + * > + * @since 1.0 > + */ > + public static final int DEFAULT_MINIMUM_CODE_POINT = 0; > + > + private int minimumCodePoint = DEFAULT_MINIMUM_CODE_POINT; > + private int maximumCodePoint = DEFAULT_MAXIMUM_CODE_POINT; > + private Set<CharacterPredicate> inclusivePredicates; > + private Random random; > + > + > + /** > + * <p> > + * Specifies the minimum and maximum code points allowed in the > generated > + * string. > + * </p> > + * > + * @param minimumCodePoint > + * the smallest code point allowed (inclusive) > + * @param maximumCodePoint > + * the largest code point allowed (inclusive) > + * @return {@code this}, to allow method chaining > + * @throws IllegalArgumentException > + * if {@code maximumCodePoint >} > + * {@link Character#MAX_CODE_POINT} > + * @throws IllegalArgumentException > + * if {@code minimumCodePoint < 0} > + * @throws IllegalArgumentException > + * if {@code minimumCodePoint > maximumCodePoint} > + * @since 1.0 > + */ > + public Builder withinRange(final int minimumCodePoint, final int > maximumCodePoint) { > + if (minimumCodePoint > maximumCodePoint) { > + throw new IllegalArgumentException(String.format( > + "Minimum code point %d is larger than maximum code > point %d", minimumCodePoint, maximumCodePoint)); > + } > + if (minimumCodePoint < 0) { > + throw new IllegalArgumentException(String.format("Minimum > code point %d is negative", minimumCodePoint)); > + } > + if (maximumCodePoint > Character.MAX_CODE_POINT) { > + throw new IllegalArgumentException( > + String.format("Value %d is larger than > Character.MAX_CODE_POINT.", maximumCodePoint)); > + } > + > + this.minimumCodePoint = minimumCodePoint; > + this.maximumCodePoint = maximumCodePoint; > + return this; > + } > + > + /** > + * <p> > + * Limits the characters in the generated string to those that match > at > + * least one of the predicates supplied. > + * </p> > + * > + * <p> > + * Passing {@code null} or an empty array to this method will revert > to the > + * default behaviour of allowing any character. Multiple calls to > this > + * method will replace the previously stored predicates. > + * </p> > + * > + * @param predicates > + * the predicates, may be {@code null} or empty > + * @return {@code this}, to allow method chaining > + * @since 1.0 > + */ > + public Builder filteredBy(final CharacterPredicate... predicates) { > + if (predicates == null || predicates.length == 0) { > + inclusivePredicates = null; > + return this; > + } > + > + if (inclusivePredicates == null) { > + inclusivePredicates = new HashSet<>(); > + } else { > + inclusivePredicates.clear(); > + } > + > + for (CharacterPredicate predicate : predicates) { > + inclusivePredicates.add(predicate); > + } > + > + return this; > + } > + > + /** > + * <p> > + * Overrides the default source of randomness. > + * </p> > + * > + * <p> > + * Passing {@code null} to this method will revert to the default > source of > + * randomness. > + * </p> > + * > + * @param random > + * the source of randomness, may be {@code null} > + * @return {@code this}, to allow method chaining > + * @since 1.0 > + */ > + public Builder usingRandom(final Random random) { > + this.random = random; > + return this; > + } > + > + /** > + * <p>Builds the {@code RandomStringGenerator} using the properties > specified.</p> > + */ > + @Override > + public RandomStringGenerator build() { > + return new RandomStringGenerator(minimumCodePoint, > maximumCodePoint, inclusivePredicates, random); > + } > + > + } > +} > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/test/java/org/apache/commons/text/CharacterPredicatesTest.java > ---------------------------------------------------------------------- > diff --git > a/src/test/java/org/apache/commons/text/CharacterPredicatesTest.java > b/src/test/java/org/apache/commons/text/CharacterPredicatesTest.java > new file mode 100644 > index 0000000..3ff8b74 > --- /dev/null > +++ b/src/test/java/org/apache/commons/text/CharacterPredicatesTest.java > @@ -0,0 +1,50 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache License, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > +package org.apache.commons.text; > + > +import static org.junit.Assert.assertTrue; > + > +import org.junit.Test; > + > +/** > + * Tests for implementations in the {@link CharacterPredicates} enum. > + */ > +public class CharacterPredicatesTest { > + @Test > + public void testDigitPredicate() throws Exception { > + String str = new > RandomStringGenerator.Builder().filteredBy(CharacterPredicates.DIGITS).build().generate(5000); > + > + int i = 0; > + do { > + int codePoint = str.codePointAt(i); > + assertTrue(Character.isDigit(codePoint)); > + i += Character.charCount(codePoint); > + } while (i < str.length()); > + } > + > + @Test > + public void testLetterPredicate() throws Exception { > + String str = new > RandomStringGenerator.Builder().filteredBy(CharacterPredicates.LETTERS).build().generate(5000); > + > + int i = 0; > + do { > + int codePoint = str.codePointAt(i); > + assertTrue(Character.isLetter(codePoint)); > + i += Character.charCount(codePoint); > + } while (i < str.length()); > + } > +} > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/test/java/org/apache/commons/text/RandomStringBuilderTest.java > ---------------------------------------------------------------------- > diff --git > a/src/test/java/org/apache/commons/text/RandomStringBuilderTest.java > b/src/test/java/org/apache/commons/text/RandomStringBuilderTest.java > deleted file mode 100644 > index e6f9f81..0000000 > --- a/src/test/java/org/apache/commons/text/RandomStringBuilderTest.java > +++ /dev/null > @@ -1,235 +0,0 @@ > -/* > - * Licensed to the Apache Software Foundation (ASF) under one or more > - * contributor license agreements. See the NOTICE file distributed with > - * this work for additional information regarding copyright ownership. > - * The ASF licenses this file to You under the Apache License, Version 2.0 > - * (the "License"); you may not use this file except in compliance with > - * the License. You may obtain a copy of the License at > - * > - * http://www.apache.org/licenses/LICENSE-2.0 > - * > - * Unless required by applicable law or agreed to in writing, software > - * distributed under the License is distributed on an "AS IS" BASIS, > - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > - * See the License for the specific language governing permissions and > - * limitations under the License. > - */ > -package org.apache.commons.text; > - > -import static org.apache.commons.text.RandomStringBuilder.LETTERS; > -import static org.junit.Assert.*; > - > -import java.util.Random; > - > -import org.apache.commons.text.RandomStringBuilder.CodePointPredicate; > -import org.junit.Test; > - > -/** > - * Tests for {@link RandomStringBuilder} > - */ > -public class RandomStringBuilderTest { > - > - private static int codePointLength(String s) { > - return s.codePointCount(0, s.length()); > - } > - > - private static final CodePointPredicate A_FILTER = new > CodePointPredicate() { > - @Override > - public boolean test(int codePoint) { > - return codePoint == 'a'; > - } > - }; > - > - private static final CodePointPredicate B_FILTER = new > CodePointPredicate() { > - @Override > - public boolean test(int codePoint) { > - return codePoint == 'b'; > - } > - }; > - > - @Test > - public void testDefaultLength() throws Exception { > - String str = new RandomStringBuilder().build(); > - assertEquals(RandomStringBuilder.DEFAULT_LENGTH, > codePointLength(str)); > - } > - > - @Test(expected = IllegalArgumentException.class) > - public void testInvalidLength() throws Exception { > - new RandomStringBuilder().ofLength(-1); > - } > - > - @Test > - public void testSetLength() throws Exception { > - final int length = 99; > - String str = new RandomStringBuilder().ofLength(length).build(); > - assertEquals(length, codePointLength(str)); > - } > - > - @Test(expected = IllegalArgumentException.class) > - public void testBadMinimumCodePoint() throws Exception { > - new RandomStringBuilder().withinRange(-1, 1); > - } > - > - @Test(expected = IllegalArgumentException.class) > - public void testBadMaximumCodePoint() throws Exception { > - new RandomStringBuilder().withinRange(0, Character.MAX_CODE_POINT + > 1); > - } > - > - @Test > - public void testWithinRange() throws Exception { > - final int length = 5000; > - final int minimumCodePoint = 'a'; > - final int maximumCodePoint = 'z'; > - String str = new > RandomStringBuilder().ofLength(length).withinRange(minimumCodePoint,maximumCodePoint).build(); > - > - int i = 0; > - do { > - int codePoint = str.codePointAt(i); > - assertTrue(codePoint >= minimumCodePoint && codePoint <= > maximumCodePoint); > - i += Character.charCount(codePoint); > - } while (i < str.length()); > - > - } > - > - @Test > - public void testNoLoneSurrogates() throws Exception { > - final int length = 5000; > - String str = new RandomStringBuilder().ofLength(length).build(); > - > - char lastChar = str.charAt(0); > - for (int i = 1; i < str.length(); i++) { > - char c = str.charAt(i); > - > - if (Character.isLowSurrogate(c)) { > - assertTrue(Character.isHighSurrogate(lastChar)); > - } > - > - if (Character.isHighSurrogate(lastChar)) { > - assertTrue(Character.isLowSurrogate(c)); > - } > - > - if (Character.isHighSurrogate(c)) { > - // test this isn't the last character in the string > - assertTrue(i + 1 < str.length()); > - } > - > - lastChar = c; > - } > - } > - > - @Test > - public void testUsingRandom() throws Exception { > - final char testChar = 'a'; > - final Random testRandom = new Random() { > - private static final long serialVersionUID = 1L; > - > - @Override > - public int nextInt(int n) { > - return testChar; > - } > - }; > - > - String str = new > RandomStringBuilder().ofLength(100).usingRandom(testRandom).build(); > - for (char c : str.toCharArray()) { > - assertEquals(testChar, c); > - } > - } > - > - @Test > - public void testLetterPredicate() throws Exception { > - String str = new > RandomStringBuilder().ofLength(5000).filteredBy(LETTERS).build(); > - > - int i = 0; > - do { > - int codePoint = str.codePointAt(i); > - assertTrue(Character.isLetter(codePoint)); > - i += Character.charCount(codePoint); > - } while (i < str.length()); > - } > - > - @Test > - public void testDigitPredicate() throws Exception { > - String str = new > RandomStringBuilder().ofLength(5000).filteredBy(RandomStringBuilder.DIGITS).build(); > - > - int i = 0; > - do { > - int codePoint = str.codePointAt(i); > - assertTrue(Character.isDigit(codePoint)); > - i += Character.charCount(codePoint); > - } while (i < str.length()); > - } > - > - @Test > - public void testMultipleFilters() throws Exception { > - String str = new > RandomStringBuilder().ofLength(5000).withinRange('a','d') > - .filteredBy(A_FILTER, B_FILTER).build(); > - > - boolean aFound = false; > - boolean bFound = false; > - > - for (char c : str.toCharArray()) { > - if (c == 'a') { > - aFound = true; > - } else if (c == 'b') { > - bFound = true; > - } else { > - fail("Invalid character"); > - } > - } > - > - assertTrue(aFound && bFound); > - } > - > - @Test > - public void testNoPrivateCharacters() throws Exception { > - final int startOfPrivateBMPChars = 0xE000; > - > - // Request a string in an area of the Basic Multilingual Plane that > is > - // largely > - // occupied by private characters > - String str = new > RandomStringBuilder().ofLength(5000).withinRange(startOfPrivateBMPChars, > - Character.MIN_SUPPLEMENTARY_CODE_POINT - 1).build(); > - > - int i = 0; > - do { > - int codePoint = str.codePointAt(i); > - assertFalse(Character.getType(codePoint) == > Character.PRIVATE_USE); > - i += Character.charCount(codePoint); > - } while (i < str.length()); > - } > - > - @Test(expected = IllegalArgumentException.class) > - public void testBadMinAndMax() throws Exception { > - new RandomStringBuilder().withinRange(2, 1); > - } > - > - @Test > - public void testRemoveFilters() throws Exception { > - > - RandomStringBuilder builder = new > RandomStringBuilder().ofLength(100).withinRange('a', 'z') > - .filteredBy(A_FILTER); > - > - builder.filteredBy(); > - > - String str = builder.build(); > - for (char c : str.toCharArray()) { > - if (c != 'a') { > - // filter was successfully removed > - return; > - } > - } > - > - fail("Filter appears to have remained in place"); > - } > - > - @Test > - public void testChangeOfFilter() throws Exception { > - RandomStringBuilder builder = new > RandomStringBuilder().ofLength(100).withinRange('a', 'z') > - .filteredBy(A_FILTER); > - String str = builder.filteredBy(B_FILTER).build(); > - > - for (char c : str.toCharArray()) { > - assertTrue(c == 'b'); > - } > - } > -} > > http://git-wip-us.apache.org/repos/asf/commons-text/blob/08ac56a5/src/test/java/org/apache/commons/text/RandomStringGeneratorTest.java > ---------------------------------------------------------------------- > diff --git > a/src/test/java/org/apache/commons/text/RandomStringGeneratorTest.java > b/src/test/java/org/apache/commons/text/RandomStringGeneratorTest.java > new file mode 100644 > index 0000000..2ff7a67 > --- /dev/null > +++ b/src/test/java/org/apache/commons/text/RandomStringGeneratorTest.java > @@ -0,0 +1,206 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache License, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > +package org.apache.commons.text; > + > +import static org.junit.Assert.*; > + > +import java.util.Random; > + > +import org.junit.Test; > + > +/** > + * Tests for {@link RandomStringGenerator} > + */ > +public class RandomStringGeneratorTest { > + > + private static int codePointLength(String s) { > + return s.codePointCount(0, s.length()); > + } > + > + private static final CharacterPredicate A_FILTER = new > CharacterPredicate() { > + @Override > + public boolean test(int codePoint) { > + return codePoint == 'a'; > + } > + }; > + > + private static final CharacterPredicate B_FILTER = new > CharacterPredicate() { > + @Override > + public boolean test(int codePoint) { > + return codePoint == 'b'; > + } > + }; > + > + @Test(expected = IllegalArgumentException.class) > + public void testInvalidLength() throws Exception { > + RandomStringGenerator generator = new > RandomStringGenerator.Builder().build(); > + generator.generate(-1); > + } > + > + @Test > + public void testSetLength() throws Exception { > + final int length = 99; > + RandomStringGenerator generator = new > RandomStringGenerator.Builder().build(); > + String str = generator.generate(length); > + assertEquals(length, codePointLength(str)); > + } > + > + @Test(expected = IllegalArgumentException.class) > + public void testBadMinimumCodePoint() throws Exception { > + new RandomStringGenerator.Builder().withinRange(-1, 1); > + } > + > + @Test(expected = IllegalArgumentException.class) > + public void testBadMaximumCodePoint() throws Exception { > + new RandomStringGenerator.Builder().withinRange(0, > Character.MAX_CODE_POINT + 1); > + } > + > + @Test > + public void testWithinRange() throws Exception { > + final int length = 5000; > + final int minimumCodePoint = 'a'; > + final int maximumCodePoint = 'z'; > + RandomStringGenerator generator = new > RandomStringGenerator.Builder().withinRange(minimumCodePoint,maximumCodePoint).build(); > + String str = generator.generate(length); > + > + int i = 0; > + do { > + int codePoint = str.codePointAt(i); > + assertTrue(codePoint >= minimumCodePoint && codePoint <= > maximumCodePoint); > + i += Character.charCount(codePoint); > + } while (i < str.length()); > + > + } > + > + @Test > + public void testNoLoneSurrogates() throws Exception { > + final int length = 5000; > + String str = new > RandomStringGenerator.Builder().build().generate(length); > + > + char lastChar = str.charAt(0); > + for (int i = 1; i < str.length(); i++) { > + char c = str.charAt(i); > + > + if (Character.isLowSurrogate(c)) { > + assertTrue(Character.isHighSurrogate(lastChar)); > + } > + > + if (Character.isHighSurrogate(lastChar)) { > + assertTrue(Character.isLowSurrogate(c)); > + } > + > + if (Character.isHighSurrogate(c)) { > + // test this isn't the last character in the string > + assertTrue(i + 1 < str.length()); > + } > + > + lastChar = c; > + } > + } > + > + @Test > + public void testUsingRandom() throws Exception { > + final char testChar = 'a'; > + final Random testRandom = new Random() { > + private static final long serialVersionUID = 1L; > + > + @Override > + public int nextInt(int n) { > + return testChar; > + } > + }; > + > + String str = new > RandomStringGenerator.Builder().usingRandom(testRandom).build().generate(10); > + for (char c : str.toCharArray()) { > + assertEquals(testChar, c); > + } > + } > + > + @Test > + public void testMultipleFilters() throws Exception { > + String str = new RandomStringGenerator.Builder().withinRange('a','d') > + .filteredBy(A_FILTER, B_FILTER).build().generate(5000); > + > + boolean aFound = false; > + boolean bFound = false; > + > + for (char c : str.toCharArray()) { > + if (c == 'a') { > + aFound = true; > + } else if (c == 'b') { > + bFound = true; > + } else { > + fail("Invalid character"); > + } > + } > + > + assertTrue(aFound && bFound); > + } > + > + @Test > + public void testNoPrivateCharacters() throws Exception { > + final int startOfPrivateBMPChars = 0xE000; > + > + // Request a string in an area of the Basic Multilingual Plane that > is > + // largely > + // occupied by private characters > + String str = new > RandomStringGenerator.Builder().withinRange(startOfPrivateBMPChars, > + Character.MIN_SUPPLEMENTARY_CODE_POINT - > 1).build().generate(5000); > + > + int i = 0; > + do { > + int codePoint = str.codePointAt(i); > + assertFalse(Character.getType(codePoint) == > Character.PRIVATE_USE); > + i += Character.charCount(codePoint); > + } while (i < str.length()); > + } > + > + @Test(expected = IllegalArgumentException.class) > + public void testBadMinAndMax() throws Exception { > + new RandomStringGenerator.Builder().withinRange(2, 1); > + } > + > + @Test > + public void testRemoveFilters() throws Exception { > + > + RandomStringGenerator.Builder builder = new > RandomStringGenerator.Builder().withinRange('a', 'z') > + .filteredBy(A_FILTER); > + > + builder.filteredBy(); > + > + String str = builder.build().generate(100); > + for (char c : str.toCharArray()) { > + if (c != 'a') { > + // filter was successfully removed > + return; > + } > + } > + > + fail("Filter appears to have remained in place"); > + } > + > + @Test > + public void testChangeOfFilter() throws Exception { > + RandomStringGenerator.Builder builder = new > RandomStringGenerator.Builder().withinRange('a', 'z') > + .filteredBy(A_FILTER); > + String str = builder.filteredBy(B_FILTER).build().generate(100); > + > + for (char c : str.toCharArray()) { > + assertTrue(c == 'b'); > + } > + } > +} > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org