PatternRandomField.java
package de.slothsoft.random.types;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import de.slothsoft.random.RandomField;
/**
 * A field where you define a pattern and have a couple of random field to fill the
 * pattern with.
 *
 * @author Stef Schulz
 * @since 2.1.0
 */
public class PatternRandomField implements RandomField {
	private final Map<String, RandomField> components = new HashMap<>();
	private String pattern;
	private double nullProbability;
	/**
	 * Default constructor.
	 *
	 * @param pattern the pattern to fill
	 * @see #addComponent(String, RandomField)
	 */
	public PatternRandomField(String pattern) {
		this.pattern = Objects.requireNonNull(pattern);
	}
	@Override
	public String nextValue() {
		if (RND.nextDouble() < this.nullProbability) {
			return null;
		}
		String result = this.pattern;
		for (final Entry<String, RandomField> component : this.components.entrySet()) {
			final String key = component.getKey();
			int index = result.indexOf(key);
			while (index >= 0) {
				final Object value = component.getValue().nextValue();
				final String valueAsString = value == null ? "" : value.toString();
				result = result.substring(0, index) + valueAsString + result.substring(index + key.length());
				index = result.indexOf(key);
			}
		}
		return result;
	}
	/**
	 * Adds a component that can be accessed by {@link #nextValue()}.
	 *
	 * @param key the key that is replaced "as is"
	 * @param randomField the random field to replace this key with
	 */
	public void addComponent(String key, RandomField randomField) {
		this.components.put(key, randomField);
	}
	/**
	 * Adds components that can be accessed by {@link #nextValue()}.
	 *
	 * @param addedComponents with keys and random fields
	 * @see #addComponent(String, RandomField)
	 */
	public void addComponents(Map<String, RandomField> addedComponents) {
		this.components.putAll(addedComponents);
	}
	/**
	 * Removes a component that can be accessed by {@link #nextValue()}.
	 *
	 * @param key the key that is replaced "as is"
	 */
	public void removeComponent(String key) {
		this.components.remove(key);
	}
	/**
	 * Removes components that can be accessed by {@link #nextValue()}.
	 *
	 * @param keys the keys
	 * @see #removeComponent(String)
	 */
	public void removeComponents(Set<String> keys) {
		keys.forEach(this::removeComponent);
	}
	/**
	 * Returns the pattern with keys that get replaced during {@link #nextValue()}.
	 *
	 * @return the pattern
	 */
	public String getPattern() {
		return this.pattern;
	}
	/**
	 * Sets the pattern with keys that get replaced during {@link #nextValue()}.
	 *
	 * @param newPattern the pattern
	 * @return this instance
	 */
	public PatternRandomField pattern(String newPattern) {
		setPattern(newPattern);
		return this;
	}
	/**
	 * Sets the pattern with keys that get replaced during {@link #nextValue()}.
	 *
	 * @param pattern the pattern
	 */
	public void setPattern(String pattern) {
		this.pattern = Objects.requireNonNull(pattern);
	}
	/**
	 * Returns the probability for this field returning null. If the value is 0 then no
	 * {@link #nextValue()} is null, if it is 1 then every {@link #nextValue()} is null.
	 *
	 * @return the probability between 0 and 1
	 */
	public double getNullProbability() {
		return this.nullProbability;
	}
	/**
	 * Sets the probability for this field returning null. If the value is 0 then no
	 * {@link #nextValue()} is null, if it is 1 then every {@link #nextValue()} is null.
	 *
	 * @param newNullProbability the probability between 0 and 1
	 * @return this instance
	 */
	public PatternRandomField nullProbability(double newNullProbability) {
		setNullProbability(newNullProbability);
		return this;
	}
	/**
	 * Sets the probability for this field returning null. If the value is 0 then no
	 * {@link #nextValue()} is null, if it is 1 then every {@link #nextValue()} is null.
	 *
	 * @param nullProbability the probability between 0 and 1
	 */
	public void setNullProbability(double nullProbability) {
		if (nullProbability < 0 || nullProbability > 1) {
			throw new IllegalArgumentException("Null probability must be between 0 and 1!");
		}
		this.nullProbability = nullProbability;
	}
}