| /* |
| * 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 org.apache.commons.lang3.Validate; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.Collections; |
| 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 randomLetters = generator.generate(20); |
| * </pre> |
| * <pre> |
| * // Using Apache Commons RNG for randomness |
| * UniformRandomProvider rng = RandomSource.create(...); |
| * // Generates a 20 code point string, using only the letters a-z |
| * RandomStringGenerator generator = new RandomStringGenerator.Builder() |
| * .withinRange('a', 'z') |
| * .usingRandom(rng::nextInt) // uses Java 8 syntax |
| * .build(); |
| * String randomLetters = generator.generate(20); |
| * </pre> |
| * <p> |
| * {@code RandomStringGenerator} instances are thread-safe when using the |
| * default random number generator (RNG). If a custom RNG is set by calling the method |
| * {@link Builder#usingRandom(TextRandomProvider) Builder.usingRandom(TextRandomProvider)}, thread-safety |
| * must be ensured externally. |
| * </p> |
| * @since 1.1 |
| */ |
| public final class RandomStringGenerator { |
| |
| /** |
| * The smallest allowed code point (inclusive). |
| */ |
| private final int minimumCodePoint; |
| |
| /** |
| * The largest allowed code point (inclusive). |
| */ |
| private final int maximumCodePoint; |
| |
| /** |
| * Filters for code points. |
| */ |
| private final Set<CharacterPredicate> inclusivePredicates; |
| |
| /** |
| * The source of randomness for this generator. |
| */ |
| private final TextRandomProvider random; |
| |
| /** |
| * The source of provided characters. |
| */ |
| private final List<Character> characterList; |
| |
| /** |
| * 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 |
| * @param characterList list of predefined set of characters. |
| */ |
| private RandomStringGenerator(final int minimumCodePoint, final int maximumCodePoint, |
| final Set<CharacterPredicate> inclusivePredicates, final TextRandomProvider random, |
| final List<Character> characterList) { |
| this.minimumCodePoint = minimumCodePoint; |
| this.maximumCodePoint = maximumCodePoint; |
| this.inclusivePredicates = inclusivePredicates; |
| this.random = random; |
| this.characterList = characterList; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Generates a random number within a range, using a {@link ThreadLocalRandom} instance |
| * or the user-supplied source of randomness. |
| * |
| * @param characterList predefined char list. |
| * @return the random number. |
| */ |
| private int generateRandomNumber(final List<Character> characterList) { |
| final int listSize = characterList.size(); |
| if (random != null) { |
| return String.valueOf(characterList.get(random.nextInt(listSize))).codePointAt(0); |
| } |
| return String.valueOf(characterList.get(ThreadLocalRandom.current().nextInt(0, listSize))).codePointAt(0); |
| } |
| |
| /** |
| * <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} |
| */ |
| public String generate(final int length) { |
| if (length == 0) { |
| return ""; |
| } |
| Validate.isTrue(length > 0, "Length %d is smaller than zero.", length); |
| |
| final StringBuilder builder = new StringBuilder(length); |
| long remaining = length; |
| |
| do { |
| int codePoint; |
| if (characterList != null && !characterList.isEmpty()) { |
| codePoint = generateRandomNumber(characterList); |
| } else { |
| codePoint = generateRandomNumber(minimumCodePoint, maximumCodePoint); |
| } |
| switch (Character.getType(codePoint)) { |
| case Character.UNASSIGNED: |
| case Character.PRIVATE_USE: |
| case Character.SURROGATE: |
| continue; |
| default: |
| } |
| |
| if (inclusivePredicates != null) { |
| boolean matchedFilter = false; |
| for (final CharacterPredicate predicate : inclusivePredicates) { |
| if (predicate.test(codePoint)) { |
| matchedFilter = true; |
| break; |
| } |
| } |
| if (!matchedFilter) { |
| continue; |
| } |
| } |
| |
| builder.appendCodePoint(codePoint); |
| remaining--; |
| |
| } while (remaining != 0); |
| |
| return builder.toString(); |
| } |
| |
| /** |
| * Generates a random string, containing between the minimum (inclusive) and the maximum (inclusive) |
| * number of code points. |
| * |
| * @param minLengthInclusive |
| * the minimum (inclusive) number of code points to generate |
| * @param maxLengthInclusive |
| * the maximum (inclusive) number of code points to generate |
| * @return the generated string |
| * @throws IllegalArgumentException |
| * if {@code minLengthInclusive < 0}, or {@code maxLengthInclusive < minLengthInclusive} |
| * @see RandomStringGenerator#generate(int) |
| * @since 1.2 |
| */ |
| public String generate(final int minLengthInclusive, final int maxLengthInclusive) { |
| Validate.isTrue(minLengthInclusive >= 0, "Minimum length %d is smaller than zero.", minLengthInclusive); |
| Validate.isTrue(minLengthInclusive <= maxLengthInclusive, |
| "Maximum length %d is smaller than minimum length %d.", maxLengthInclusive, minLengthInclusive); |
| return generate(generateRandomNumber(minLengthInclusive, maxLengthInclusive)); |
| } |
| |
| /** |
| * <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(TextRandomProvider)}, |
| * 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.1 |
| */ |
| public static class Builder implements org.apache.commons.text.Builder<RandomStringGenerator> { |
| |
| /** |
| * The default maximum code point allowed: {@link Character#MAX_CODE_POINT} |
| * ({@value}). |
| */ |
| public static final int DEFAULT_MAXIMUM_CODE_POINT = Character.MAX_CODE_POINT; |
| |
| /** |
| * The default string length produced by this builder: {@value}. |
| */ |
| public static final int DEFAULT_LENGTH = 0; |
| |
| /** |
| * The default minimum code point allowed: {@value}. |
| */ |
| public static final int DEFAULT_MINIMUM_CODE_POINT = 0; |
| |
| /** |
| * The minimum code point allowed. |
| */ |
| private int minimumCodePoint = DEFAULT_MINIMUM_CODE_POINT; |
| |
| /** |
| * The maximum code point allowed. |
| */ |
| private int maximumCodePoint = DEFAULT_MAXIMUM_CODE_POINT; |
| |
| /** |
| * Filters for code points. |
| */ |
| private Set<CharacterPredicate> inclusivePredicates; |
| |
| /** |
| * The source of randomness. |
| */ |
| private TextRandomProvider random; |
| |
| /** |
| * The source of provided characters. |
| */ |
| private List<Character> characterList; |
| |
| /** |
| * <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} |
| */ |
| public Builder withinRange(final int minimumCodePoint, final int maximumCodePoint) { |
| Validate.isTrue(minimumCodePoint <= maximumCodePoint, |
| "Minimum code point %d is larger than maximum code point %d", minimumCodePoint, maximumCodePoint); |
| Validate.isTrue(minimumCodePoint >= 0, "Minimum code point %d is negative", minimumCodePoint); |
| Validate.isTrue(maximumCodePoint <= Character.MAX_CODE_POINT, |
| "Value %d is larger than Character.MAX_CODE_POINT.", maximumCodePoint); |
| |
| this.minimumCodePoint = minimumCodePoint; |
| this.maximumCodePoint = maximumCodePoint; |
| return this; |
| } |
| |
| /** |
| * <p> |
| * Specifies the array of minimum and maximum char allowed in the |
| * generated string. |
| * </p> |
| * |
| * For example: |
| * <pre> |
| * {@code |
| * char [][] pairs = {{'0','9'}}; |
| * char [][] pairs = {{'a','z'}}; |
| * char [][] pairs = {{'a','z'},{'0','9'}}; |
| * } |
| * </pre> |
| * |
| * @param pairs array of characters array, expected is to pass min, max pairs through this arg. |
| * @return {@code this}, to allow method chaining. |
| */ |
| public Builder withinRange(final char[]... pairs) { |
| characterList = new ArrayList<>(); |
| for (final char[] pair : pairs) { |
| Validate.isTrue(pair.length == 2, |
| "Each pair must contain minimum and maximum code point"); |
| final int minimumCodePoint = pair[0]; |
| final int maximumCodePoint = pair[1]; |
| Validate.isTrue(minimumCodePoint <= maximumCodePoint, |
| "Minimum code point %d is larger than maximum code point %d", minimumCodePoint, maximumCodePoint); |
| |
| for (int index = minimumCodePoint; index <= maximumCodePoint; index++) { |
| characterList.add((char) index); |
| } |
| } |
| 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 |
| */ |
| 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(); |
| } |
| |
| Collections.addAll(inclusivePredicates, predicates); |
| |
| return this; |
| } |
| |
| /** |
| * <p> |
| * Overrides the default source of randomness. It is highly |
| * recommended that a random number generator library like |
| * <a href="http://commons.apache.org/proper/commons-rng/">Apache Commons RNG</a> |
| * be used to provide the random number generation. |
| * </p> |
| * |
| * <p> |
| * When using Java 8 or later, {@link TextRandomProvider} is a |
| * functional interface and need not be explicitly implemented: |
| * </p> |
| * <pre> |
| * {@code |
| * UniformRandomProvider rng = RandomSource.create(...); |
| * RandomStringGenerator gen = new RandomStringGenerator.Builder() |
| * .usingRandom(rng::nextInt) |
| * // additional builder calls as needed |
| * .build(); |
| * } |
| * </pre> |
| * |
| * <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 |
| */ |
| public Builder usingRandom(final TextRandomProvider random) { |
| this.random = random; |
| return this; |
| } |
| |
| /** |
| * <p> |
| * Limits the characters in the generated string to those who match at |
| * supplied list of Character. |
| * </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 Character. |
| * </p> |
| * |
| * @param chars set of predefined Characters for random string generation |
| * the Character can be, may be {@code null} or empty |
| * @return {@code this}, to allow method chaining |
| * @since 1.2 |
| */ |
| public Builder selectFrom(final char... chars) { |
| characterList = new ArrayList<>(); |
| for (final char c : chars) { |
| characterList.add(c); |
| } |
| return this; |
| } |
| |
| /** |
| * <p>Builds the {@code RandomStringGenerator} using the properties specified.</p> |
| * @return the configured {@code RandomStringGenerator} |
| */ |
| @Override |
| public RandomStringGenerator build() { |
| return new RandomStringGenerator(minimumCodePoint, maximumCodePoint, inclusivePredicates, |
| random, characterList); |
| } |
| } |
| } |