AbstractChronoRandomField.java

package de.slothsoft.random.types;

import java.util.Objects;

import de.slothsoft.random.RandomField;

/**
 * A {@link RandomField} representing a date, time, date time or anything time related.
 *
 * @author Stef Schulz
 * @since 2.1.0
 * @param <C> the date and / or time
 */

abstract class AbstractChronoRandomField<C> implements RandomField {

	private C startValue;
	private C endValue;
	private double nullProbability;

	@Override
	public C nextValue() {
		if (RND.nextDouble() < this.nullProbability) {
			return null;
		}

		final C start = this.startValue == null ? createDefaultStartValue() : this.startValue;
		final C end = this.endValue == null ? createDefaultEndValue() : this.endValue;

		if (end.equals(start)) {
			return start;
		}

		final long startValueAsLong = toLongValue(start);
		final long endValueAsLong = toLongValue(end);

		final long result = startValueAsLong + (long) (RND.nextDouble() * (endValueAsLong - startValueAsLong));
		return fromLongValue(result);
	}

	abstract long toLongValue(C value);

	abstract C fromLongValue(long value);

	abstract C createDefaultStartValue();

	abstract C createDefaultEndValue();

	boolean isBefore(C value1, C value2) {
		return toLongValue(value1) < toLongValue(value2);
	}

	boolean isEqual(C value1, C value2) {
		return toLongValue(value1) == toLongValue(value2);
	}

	boolean isAfter(C value1, C value2) {
		return toLongValue(value1) > toLongValue(value2);
	}

	/**
	 * Returns the highest possible value for this field.
	 *
	 * @return end value
	 */

	public C getEndValue() {
		return this.endValue;
	}

	/**
	 * Sets the highest possible value for this field.
	 *
	 * @param newEndValue end value
	 * @return this instance
	 */

	public AbstractChronoRandomField<C> endValue(C newEndValue) {
		setEndValue(newEndValue);
		return this;
	}

	/**
	 * Sets the highest possible value for this field.
	 *
	 * @param endValue end value
	 */

	public void setEndValue(C endValue) {
		this.endValue = Objects.requireNonNull(endValue);
	}

	/**
	 * Returns the lowest possible value for this field.
	 *
	 * @return start value
	 */

	public C getStartValue() {
		return this.startValue;
	}

	/**
	 * Sets the lowest possible value for this field.
	 *
	 * @param newStartValue start value
	 * @return this instance
	 */

	public AbstractChronoRandomField<C> startValue(C newStartValue) {
		setStartValue(newStartValue);
		return this;
	}

	/**
	 * Sets the lowest possible value for this field.
	 *
	 * @param startValue start value
	 */

	public void setStartValue(C startValue) {
		this.startValue = Objects.requireNonNull(startValue);
	}

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

}