CollectionRandomField.java

package de.slothsoft.random.types;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Supplier;

import de.slothsoft.random.RandomField;

/**
 * A {@link RandomField} representing an {@link Collection}.
 *
 * @author Stef Schulz
 * @since 2.1.0
 */

public class CollectionRandomField implements RandomField {

	/**
	 * Returns a {@link Supplier} for creating instances of a collection class. Might
	 * return null.
	 *
	 * @param collectionClass a collection class
	 * @return a {@link Supplier} or null
	 */

	static Supplier<Collection<?>> createCollectionConstructor(Class<?> collectionClass) {
		if (collectionClass == List.class) {
			return ArrayList::new;
		}
		if (collectionClass == Set.class) {
			return TreeSet::new;
		}
		if (collectionClass == Queue.class) {
			return LinkedList::new;
		}
		if (collectionClass == Collection.class) {
			return ArrayList::new;
		}
		return null;
	}

	private final Supplier<Collection<?>> collectionConstructor;
	private final RandomField elementRandomField;

	private double nullProbability;
	private int collectionSize = 3;

	/**
	 * Constructor.
	 *
	 * @param collectionConstructor constructor to create the collection
	 * @param elementRandomField a generator for the elements
	 */

	public CollectionRandomField(Supplier<Collection<?>> collectionConstructor, RandomField elementRandomField) {
		this.collectionConstructor = Objects.requireNonNull(collectionConstructor);
		this.elementRandomField = Objects.requireNonNull(elementRandomField);
	}

	@Override
	@SuppressWarnings({"rawtypes", "unchecked"})
	public Object nextValue() {
		if (RND.nextDouble() < this.nullProbability) {
			return null;
		}
		final Collection result = this.collectionConstructor.get();
		final int actualCollectionSize = (int) Math.max(1, (1 + RND.nextGaussian()) * this.collectionSize);
		for (int i = 0; i < actualCollectionSize; i++) {
			result.add(this.elementRandomField.nextValue());
		}
		return result;
	}

	/**
	 * 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 CollectionRandomField 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;
	}

	/**
	 * Returns the standard collection size. A Gaussian function is used to scatter the
	 * actual collection size around this value.
	 *
	 * @return collection size
	 */

	public int getCollectionSize() {
		return this.collectionSize;
	}

	/**
	 * Sets the standard collection size. A Gaussian function is used to scatter the
	 * actual collection size around this value.
	 *
	 * @param newCollectionSize collection size
	 * @return this instance
	 */

	public CollectionRandomField collectionSize(int newCollectionSize) {
		setCollectionSize(newCollectionSize);
		return this;
	}

	/**
	 * Sets the standard collection size. A Gaussian function is used to scatter the
	 * actual collection size around this value.
	 *
	 * @param collectionSize collection size
	 */

	public void setCollectionSize(int collectionSize) {
		this.collectionSize = collectionSize;
	}

}