package org.ovirt.engine.core.utils;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/**
* <code>RandomUtils</code> is a singleton class with more powerful random generating methods.
* <P>
* Useful methods include:
* <UL>
* <LI><code>nextXXX()</code> for the <tt>byte</tt> and <tt>short</tt> types.
* <LI><code>nextXXX(mod)</code> for all types.
* <LI><code>nextXXX(min, max)</code> for all types.
* <LI><code>pickRandom()</code> - picks a random element from a given <code>List</code> or <code>Set</code>.
* </UL>
*
* @see java.util.Random
*/
@SuppressWarnings("serial")
public final class RandomUtils extends java.util.Random {
/* --- Class constants --- */
/** Error message for a case when the min is larger than max, */
private static final String MIN_MAX_ERROR = "min must be less than or equal to max";
/** The first printable character. */
private static final char FIRST_PRINTABLE_CHAR = ' ';
/** The last printable character. */
private static final char LAST_PRINTABLE_CHAR = '~';
/** The first XML printable character. */
private static final char FIRST_XML_PRINTABLE_CHAR = 'A';
/** The last XML printable character. */
private static final char LAST_XML_PRINTABLE_CHAR = 'Z';
/* --- Class Fields --- */
/** The single(ton) instance. */
private static RandomUtils instance = new RandomUtils();
/* --- Instance Fields --- */
/** The seed that was last set. */
private long seed;
/* --- Constructor --- */
/**
* Private constructor so that only we can instantiate the instance.
*/
private RandomUtils() {
}
/* --- Singleton-related Methods --- */
/**
* Returns the single(ton) instance.
*/
public static RandomUtils instance() {
return instance;
}
/**
* Returns the single(ton) instance set with the given seed.
*/
public static RandomUtils instance(long seed) {
instance.setSeed(seed);
return instance;
}
/* --- Seed-related Methods --- */
/**
* The last seed is saved, so it is possible to {@link #getSeed()} it later. Since <code>java.util.Random</code>'s
* seed is private, I am obliged to save my own copy.
*
* See {@link java.util.Random#setSeed(long)}.
*/
public synchronized void setSeed(long seed) {
super.setSeed(seed);
this.seed = seed;
}
/**
* Returns the seed that was set last.
*/
public synchronized long getSeed() {
return seed;
}
/* --- Byte-related Methods --- */
/**
* Randomizes a <tt>byte</tt> value.
*/
public byte nextByte() {
return (byte) super.nextInt();
}
/**
* Randomize a <tt>byte</tt> value between 0 (inclusive) and the specified value (exclusive).
*/
public byte nextByte(byte b) {
return (byte) super.nextInt(b);
}
/**
* Randomize a <tt>byte</tt> value in the given range [min, max].
*/
public byte nextByte(byte min, byte max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX_ERROR);
}
return (byte) (min + nextByte((byte) (max - min + 1)));
}
/* --- Short-related Methods --- */
/**
* Randomizes a <tt>short</tt> value.
*/
public short nextShort() {
return (short) super.nextInt();
}
/**
* Randomize a <tt>short</tt> value between 0 (inclusive) and the specified value (exclusive).
*/
public short nextShort(short s) {
return (short) super.nextInt(s);
}
/**
* Randomize a <tt>short</tt> value in the given range [min, max].
*/
public short nextShort(short min, short max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX_ERROR);
}
return (short) (min + nextShort((short) (max - min + 1)));
}
/* --- Integer-related Methods --- */
/**
* Randomize an <tt>int</tt> value in the given range [min, max].
*/
public int nextInt(int min, int max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX_ERROR);
}
return (min + super.nextInt(max - min + 1));
}
/* --- Long-related Methods --- */
/**
* Randomize a <tt>long</tt> value between 0 (inclusive) and the specified value (exclusive).
*/
public long nextLong(long l) {
if (l <= 0) {
throw new IllegalArgumentException("l must be greater than 0!");
}
return (Math.abs(super.nextLong()) % l);
}
/**
* Randomize a <tt>long</tt> value in the given range [min, max].
*/
public long nextLong(long min, long max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX_ERROR);
}
return (min + nextLong(max - min + 1));
}
/* --- Float-related Methods --- */
/**
* Randomize a <tt>float</tt> value between 0.0 (inclusive) and the specified value (exclusive).
*/
public float nextFloat(float f) {
return nextFloat(f, false);
}
/**
* Randomize a <tt>float</tt> value between 0.0 (inclusive) and the specified value (inclusive or exclusive as
* required).
*
* @param inclusive
* Whether or not, the returned value should include the given one.
*/
public float nextFloat(float f, boolean inclusive) {
if (f <= 0.0F) {
throw new IllegalArgumentException("f must be greater than 0!");
}
// Randomize a float
float rand = super.nextFloat();
// If the returned value should not include the given one,
// make sure that the randomized float is not exactly 1.0
if (!inclusive) {
while (rand == 1.0F) {
rand = super.nextFloat();
}
}
return (rand * f);
}
/**
* Randomize a <tt>float</tt> value in the given range [min, max].
*/
public float nextFloat(float min, float max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX_ERROR);
}
return (min + nextFloat(max - min, true));
}
/* --- Double-related Methods --- */
/**
* Randomize a <tt>double</tt> value between 0.0 (inclusive) and the specified value (exclusive).
*/
public double nextDouble(double d) {
return nextDouble(d, false);
}
/**
* Randomize a <tt>double</tt> value between 0.0 (inclusive) and the specified value (inclusive or exclusive as
* required).
*
* @param inclusive
* Whether or not, the returned value should include the given one.
*/
public double nextDouble(double d, boolean inclusive) {
if (d <= 0.0D) {
throw new IllegalArgumentException("d must be greater than 0!");
}
// Randomize a double
double rand = super.nextDouble();
// If the returned value should not include the given one,
// make sure that the randomized float is not exactly 1.0
if (!inclusive) {
while (rand == 1.0D) {
rand = super.nextDouble();
}
}
return (rand * d);
}
/* --- Collections-related Methods --- */
/**
* Picks a random element from the given <code>Collection</code>.
*/
public <T> T pickRandom(Collection<T> c) {
int elementIndex = super.nextInt(c.size());
Iterator<T> iter = c.iterator();
for (int i = 0; i < elementIndex; ++i) {
iter.next();
}
return iter.next();
}
/* --- Array-related Methods --- */
/**
* Picks a random element from the given array.
*/
public <T> T pickRandom(T[] o) {
return pickRandom(Arrays.asList(o));
}
/* --- String-related Methods --- */
/**
* Randomize a <code>String</code>.
*
* @param length
* The requested length of the string.
* @param printable
* Whether or not, the string should contain only printable characters.
*/
public String nextString(int length, boolean printable) {
if (printable) {
byte[] data = new byte[length];
for (int i = 0; i < length; ++i) {
data[i] = (byte) nextInt(
FIRST_PRINTABLE_CHAR,
LAST_PRINTABLE_CHAR);
}
return new String(data);
}
return new String(nextBytes(length));
}
/**
* Randomize a valid numeric string.
*
* @param length
* The requested length of the string.
*/
public String nextNumericString(int length) {
return Long.toString(nextLong(
(long) Math.pow(10, length - 1), (long) (Math.pow(10, length) - 1)));
}
/**
* Randomize a valid XML Element name.
*
* @param length
* The requested length of the string.
*/
public String nextXmlString(int length) {
byte[] data = new byte[length];
for (int i = 0; i < length; ++i) {
data[i] = (byte) nextInt(
FIRST_XML_PRINTABLE_CHAR,
LAST_XML_PRINTABLE_CHAR);
}
return new String(data);
}
/**
* Randomize a printable <code>String</code>.
*
* @param length
* The requested length of the string.
*/
public String nextString(int length) {
return nextString(length, true);
}
/**
* Randomize a <code>String</code> of a length in the given range [min, max].
*
* @param printable
* Whether or not, the string should contain only printable characters.
*/
public String nextString(int min, int max, boolean printable) {
return nextString(nextInt(min, max), printable);
}
/**
* Randomize a printable <code>String</code> of a length in the given range [min, max].
*/
public String nextString(int min, int max) {
return nextString(nextInt(min, max), true);
}
/* --- General Utility Methods --- */
/**
* Creates a <tt>byte</tt> array of the specified size, initialized with random values.
*/
public byte[] nextBytes(int size) {
byte[] data = new byte[size];
nextBytes(data);
return data;
}
/* -- Big Integer related methods -- */
/**
* generates a new big integer with the desired number of bits the generated number will always be positive.
*
* @param numOfBits
* the number of bits of the Big Integer
* @return the randomized big integer.
*/
public BigInteger nextBigInt(int numOfBits) {
return new BigInteger(numOfBits, this);
}
/**
* Returns a random value from an enum.
*
* @param <T>
* The enum type.
* @param enumClass
* The enum class to randomize.
*
* @return A random enum from the given enum, or null if got null.
*/
public <T extends Enum<?>> T nextEnum(Class<T> enumClass) {
if (enumClass == null) {
return null;
}
return pickRandom(enumClass.getEnumConstants());
}
}