package org.magenta.random;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
/**
* An instance of this class allow random generation of various primitives ({@code integers, shorts, longs, double}) and
* base types ({@code dates and strings}).
*
* @author ngagnon
*
*/
public class FluentRandom {
private static final int DEFAULT_DOUBLE_NUMBER_OF_DECIMAL_PLACES = 8;
private Random random;
private RandomInteger integers;
private RandomShort shorts;
private RandomDouble doubles;
private RandomLong longs;
private RandomString strings;
private RandomDate dates;
FluentRandom(){
//for cglib
}
FluentRandom(Random random) {
// use suppliers?
this.random = random;
this.longs = new RandomLong(random);
this.integers = new RandomInteger(random);
this.shorts = new RandomShort(random);
this.doubles = new RandomDouble(random, DEFAULT_DOUBLE_NUMBER_OF_DECIMAL_PLACES);
this.strings = strings("qwertyuiopasdfghjklzxcvbnm0123456789");
this.dates = new RandomDate(longs);
}
private static final Supplier<FluentRandom> SINGLETON = Suppliers.memoize(new Supplier<FluentRandom>() {
@Override
public FluentRandom get() {
return new FluentRandom(Helper.initWithDefaultRandom());
}
});
/**
* @return the singleton
*/
public static FluentRandom singleton() {
return SINGLETON.get();
}
/**
* Return a new instance of {@code Randoms} using the specified {@code random}.
*
* @param random the random
* @return a new instance
*/
public static FluentRandom get(Random random) {
return new FluentRandom(random);
}
/**
* Get the random.
* @return the random
*/
public Random getRandom() {
return this.random;
}
/**
* @return date generator
*/
public RandomDate dates() {
return this.dates;
}
/**
* @return string generator
*/
public RandomString strings() {
return this.strings;
}
/**
* @param alphabet the alphabet from which to generate strings
* @return string generator
*/
public RandomString strings(String alphabet) {
return new RandomString(alphabet, integers);
}
/**
* @return a integer generator
*/
public RandomInteger integers() {
return this.integers;
}
/**
* @param constrained the range of possible values.
* @return a integer generator
*/
public RandomInteger integers(Range<Integer> constrained) {
return new RandomInteger(random, constrained, 1);
}
/**
* @return a long generator.
*/
public RandomLong longs() {
return this.longs;
}
/**
* @param constrained the range of possible values
* @return a long generator
*/
public RandomLong longs(Range<Long> constrained) {
return new RandomLong(random, constrained, 1);
}
/**
* @return a short generator
*/
public RandomShort shorts() {
return this.shorts;
}
/**
* @param constrained the range of possible values
* @return a short generators
*/
public RandomShort shorts(Range<Short> constrained) {
return new RandomShort(random, constrained, (short) 1);
}
/**
* @return a double generator.
*/
public RandomDouble doubles() {
return this.doubles;
}
/**
* @param numberOfDecimalPlaces number of decimal places in the generated numbers
* @return a double generator
*/
public RandomDouble doubles(int numberOfDecimalPlaces) {
return new RandomDouble(random, numberOfDecimalPlaces);
}
/**
* @param numberOfDecimalPlaces number of decimal places in the generated numbers
* @param constrained the range of possible values
* @return a double generator
*/
public RandomDouble doubles(int numberOfDecimalPlaces, Range<Double> constrained) {
return new RandomDouble(random, numberOfDecimalPlaces, constrained);
}
/**
* @param values the list of values to pick from
* @param <E> the type of value
* @return a list picker
*
*/
@SafeVarargs
public final <E> RandomList<E> array(E... values) {
return new RandomList<>(random, integers(), Arrays.asList(values));
}
/**
* @param clazz the type of enum
* @param <E> type of enum
* @return a enum picker
*/
public final <E extends Enum<E>> RandomList<E> enums(Class<E> clazz) {
List<E> enums = Arrays.asList(clazz.getEnumConstants());
return new RandomList<>(random, integers(), enums);
}
/**
* @param values the collection of values to pick from
* @param <E> the type of value
* @return a list picker
*/
public <E> RandomList<E> iterable(Iterable<E> values) {
List<E> l = values instanceof List?(List<E>)values:Lists.newArrayList(values);
return new RandomList<>(random, integers(), l);
}
/**
* Return a random iterator that will iterate from the specified collection of iterable randomly.
*
* @param iterables the iterables
* @param <E> the type of value
* @return a mixed iterable
*/
public <E> Iterable<E> mix(Iterable<Iterable<? extends E>> iterables) {
return new MixedIterable<>(iterables, this);
}
private static class Helper {
private static final Logger LOG = LoggerFactory.getLogger(FluentRandom.class);
public static final String RANDOM_SEED_SYSTEM_PROPERTY_KEY = "magenta.random.seed";
// <--
// cut&paste from the Random class
private static final AtomicLong SEED_UNIQUIFIER = new AtomicLong(8682522807148012L);
private static final long M = 181783497276652981L;
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = SEED_UNIQUIFIER.get();
long next = current * M;
if (SEED_UNIQUIFIER.compareAndSet(current, next)) {
return next;
}
}
}
// -->
// end cut&paste
private static Random initWithDefaultRandom() {
LOG.info("------------------------------------------------------------------------");
LOG.info("MAGENTA RANDOM initialization");
String seedValue = System.getProperty(RANDOM_SEED_SYSTEM_PROPERTY_KEY);
Long seed = null;
if (seedValue != null) {
try {
LOG.info("The seed value read from the system property {} is {}", RANDOM_SEED_SYSTEM_PROPERTY_KEY, seedValue);
seed = Long.parseLong(seedValue);
} catch (Throwable t) {
// ignore;
LOG.info("cannot parse this value to a valid long");
}
} else {
LOG.info(
"No seed found in system properties, a new one will be generated. You can set a fixed seed for Random by setting the System Property \"{}\" to the desired value.",
RANDOM_SEED_SYSTEM_PROPERTY_KEY);
}
if (seed == null) {
seed = seedUniquifier() ^ System.nanoTime();
}
LOG.info("The seed used by magenta for random is {}", seed);
LOG.info("------------------------------------------------------------------------");
Random random = new Random(seed);
return random;
}
}
}