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(-) ---------------------------------------------------------------------- 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'); + } + } +}