All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.datafaker.providers.base.Text Maven / Gradle / Ivy

package net.datafaker.providers.base;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * Generates random text in a flexible way.
 *
 * @since 1.7.0
 */
public class Text extends AbstractProvider {

    private final Map configMap = new WeakHashMap<>();

    public Character character() {
        return text(1, 1, true).charAt(0);
    }

    public Character uppercaseCharacter() {
        return text(1, 1).toUpperCase(faker.getContext().getLocale()).charAt(0);
    }

    public Character lowercaseCharacter() {
        return text(1, 1, false).charAt(0);
    }

    /**
     * @return A lowercase string of 20 to 80 characters long.
     */
    public String text() {
        return text(false);
    }

    /**
     * @param includeDigit if digits should be included
     * @return A lowercase string of 20 to 80 characters long.
     */
    public String text(boolean includeDigit) {
        return text(20, 80, false, false, includeDigit);
    }

    /**
     * @param length The length of the string to return
     * @return A lowercase string of exact length
     */
    public String text(int length) {
        return text(length, length, false);
    }

    /**
     * @param minimumLength The minimum length (inclusive)
     * @param maximumLength The maximum length (inclusive)
     * @return A lowercase string between minimum and maximum length (inclusive)
     */
    public String text(int minimumLength, int maximumLength) {
        return text(minimumLength, maximumLength, false);
    }

    public String text(int minimumLength, int maximumLength, boolean includeUppercase) {
        return text(minimumLength, maximumLength, includeUppercase, false);
    }

    public String text(int minimumLength, int maximumLength, boolean includeUppercase, boolean includeSpecial) {
        return text(minimumLength, maximumLength, includeUppercase, includeSpecial, false);
    }

    public String text(int minimumLength, int maximumLength, boolean includeUppercase, boolean includeSpecial, boolean includeDigit) {
        final int len = faker.number().numberBetween(minimumLength, maximumLength + 1);
        TextConfigPojo pojo = new TextConfigPojo(len, includeUppercase, includeSpecial, includeDigit);
        Text.TextRuleConfig config = configMap.get(pojo);
        if (config == null) {
            TextSymbolsBuilder builder =
                TextSymbolsBuilder.builder()
                    .with(Text.EN_LOWERCASE);
            if (includeUppercase) builder = builder.with(Text.EN_UPPERCASE, 1);
            if (includeSpecial) builder = builder.with(Text.DEFAULT_SPECIAL, 1);
            if (includeDigit) builder = builder.with(Text.DIGITS, 1);

            config = builder.len(len).build();
            configMap.putIfAbsent(pojo, config);
        }


        return faker.text().text(config);
    }

    public static final String EN_LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
    public static final String EN_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public static final String DIGITS = "0123456789";
    public static final String DEFAULT_SPECIAL = "!@#$%^&*";

    public Text(BaseProviders faker) {
        super(faker);
    }

    public static class TextRuleConfig {
        private final char[][] textKeys;
        private final int[] required;
        private final int fixedNumberOfCharacters;

        private final int numberOfRequiredSymbols;

        private TextRuleConfig(int fixedNumberOfCharacters, Map map, int numberOfRequiredSymbols) {
            this.fixedNumberOfCharacters = fixedNumberOfCharacters;
            this.numberOfRequiredSymbols = numberOfRequiredSymbols;
            this.textKeys = new char[map.size()][];
            this.required = new int[map.size()];
            int i = 0;
            for (Map.Entry entry: map.entrySet()) {
                textKeys[i] = entry.getKey().toCharArray();
                required[i] = entry.getValue();
                i++;
            }
        }

        public int getFixedNumberOfCharacters() {
            return fixedNumberOfCharacters;
        }

        public int getNumberOfRequiredSymbols() {
            return numberOfRequiredSymbols;
        }

    }

    public static class TextSymbolsBuilder {
        private int length;
        private final Map map = new HashMap<>();
        private boolean throwIfLengthSmall = false;

        private TextSymbolsBuilder() {
        }

        public static TextSymbolsBuilder builder() {
            return new TextSymbolsBuilder();
        }

        public TextSymbolsBuilder with(String listOfSymbols, int times) {
            if (times < 0) {
                throw new IllegalArgumentException("times should be non-negative: " + times);
            }
            map.put(listOfSymbols, times);
            return this;
        }

        public TextSymbolsBuilder with(String listOfSymbols) {
            return with(listOfSymbols, 0);
        }

        public TextSymbolsBuilder len(int len) {
            this.length = len;
            return this;
        }

        public TextSymbolsBuilder throwIfLengthSmall(boolean throwIfLengthSmall) {
            this.throwIfLengthSmall = throwIfLengthSmall;
            return this;
        }

        public TextRuleConfig build() {
            int minSize = map.values().stream().filter(t -> t > 0).reduce(0, Integer::sum);
            if (minSize > length && throwIfLengthSmall) {
                throw new IllegalArgumentException("Min length (%s) should be not smaller than number of required characters (%s)".formatted(length, minSize));
            }
            return new TextRuleConfig(length, map, minSize);
        }
    }

    /**
     * Allows to configure custom expected rules. Example
     * 
     * {@code
     *     faker.text().text(Text.TextSymbolsBuilder.builder()
     *                 .len(5)
     *                 .with(EN_LOWERCASE, 1)
     *                 .with(EN_UPPERCASE, 1)
     *                 .with(DIGITS, 1);
     * }
     * 
* This will generate a text with length 5 containing minimum 1 lower case and 1 upper case symbol * from en locale and minimum 1 digit. * Custom symbol sets are also possible *
     * {@code
     *     final String ruLowerCase = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
     *     final String customSpecialSymbols = "!@#$%^*;'][{}";
     *     final int ruCnt = 3;
     *     final int specSmbCnt = 5;
     *     final Text.TextRuleConfig config = Text.TextSymbolsBuilder.builder()
     *         .len(faker.number().numberBetween(ruCnt + specSmbCnt, Math.max(ruCnt + specSmbCnt, 10)))
     *         .with(ruLowerCase, ruCnt)
     *         .with(customSpecialSymbols, specSmbCnt).build();
     *     final String text = faker.text().text(config);
     * }
     * 
* This will generate a string with length between 8 and 10. * The string will contain min 3 lower case symbols * from ru locale and minimum 5 symbols from the defined string var * {@code final String customSpecialSymbols = "!@#$%^*;'][{}";}. */ public String text(TextRuleConfig textRuleConfig) { final int fixedNumberOfCharacters = textRuleConfig.getFixedNumberOfCharacters(); final int numberOfRequiredSymbols = textRuleConfig.getNumberOfRequiredSymbols(); if (fixedNumberOfCharacters < numberOfRequiredSymbols) { return ""; } char[] buffer = new char[fixedNumberOfCharacters]; int idx = 0; int maxDiffSymbols = 0; for (int i = 0; i < textRuleConfig.textKeys.length; i++) { maxDiffSymbols += textRuleConfig.textKeys[i].length; } // 256 is a length of byte value range if (maxDiffSymbols <= 256) { return textWithNotMoreThan256DiffSymbols( textRuleConfig, faker.random().nextRandomBytes(2 * fixedNumberOfCharacters), fixedNumberOfCharacters, numberOfRequiredSymbols); } int numberOfRequired = 0; int[] required = Arrays.copyOf(textRuleConfig.required, textRuleConfig.required.length); while (idx < buffer.length) { if (numberOfRequiredSymbols > numberOfRequired && numberOfRequiredSymbols - numberOfRequired == buffer.length - idx) { for (int j = 0; j < textRuleConfig.textKeys.length; j++) { while (required[j] > 0) { buffer[idx++] = textRuleConfig.textKeys[j][faker.random().nextInt(textRuleConfig.textKeys[j].length)]; numberOfRequired++; required[j]--; } if (idx == buffer.length) break; } } else { int index = faker.random().nextInt(required.length); if (required[index] > 0) { numberOfRequired++; required[index]--; } buffer[idx++] = textRuleConfig.textKeys[index][faker.random().nextInt(textRuleConfig.textKeys[index].length)]; } } return String.valueOf(buffer); } private String textWithNotMoreThan256DiffSymbols( TextRuleConfig textRuleConfig, byte[] bytes, int fixedNumberOfCharacters, int numberOfRequiredSymbols) { char[] buffer = new char[fixedNumberOfCharacters]; int idx = 0; int bytesCounter = 0; int numberOfRequired = 0; int[] required = Arrays.copyOf(textRuleConfig.required, textRuleConfig.required.length); while (idx < buffer.length) { if (numberOfRequiredSymbols > numberOfRequired && numberOfRequiredSymbols - numberOfRequired == buffer.length - idx) { for (int j = 0; j < textRuleConfig.textKeys.length; j++) { while (required[j] > 0) { buffer[idx++] = textRuleConfig.textKeys[j][((char) (bytes[bytesCounter++])) % textRuleConfig.textKeys[j].length]; numberOfRequired++; required[j]--; } if (idx == buffer.length) break; } } else { int index = ((char) (bytes[bytesCounter++])) % textRuleConfig.textKeys.length; if (required[index] > 0) { numberOfRequired++; required[index]--; } buffer[idx++] = textRuleConfig.textKeys[index][((char) bytes[bytesCounter++]) % textRuleConfig.textKeys[index].length]; } } return String.valueOf(buffer); } private record TextConfigPojo(int length, boolean includeUppercase, boolean includeSpecial, boolean includeDigit) { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy