JiperRandom: A better random solution

This is an extension of java.util.Random which I find very useful and use in my tests.

Some very useful methods are: nextString, nextEnum, pickRandom and other methods for random numbers as well (which are more flexible than the standard ones in java.util.Random).



import java.math.BigInteger;
import java.util.*;

/**
 * Jiper's <code>Random</code> class adds utility methods in an attempt to 
 * corrects Sun's laziness, and produce a better solution.<P>
 * This class also corrects (what I believe to be) an historical mistake, and
 * is a <b>singleton</b> class..<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
 */
public final class JiperRandom extends java.util.Random {
    
    /**
     * The <tt>JiperRandomQualifier</tt> defines how to further qualify a value 
     * requested from {@link JiperRandom}.<br><br>
     * 
     * This class is awefully useful when you want a value is isn't completely 
     * random, but adheres to some more application-specific logic (for example:
     * a value that isn't present in a database, a palyndrom, a value with some
     * specific <tt>hashCode()</tt>, etc.)
     *  
     * @author Allon M.
     */
    public interface JiperRandomQualifier {
        
        /**
         * Checks if the parameter adheres to the logic the 
         * <tt>JiperRandomQualifier</tt> is supposed to check
         * @param o The object to check (usually, the random value generated by
         * {@link JiperRandom} 
         * @return <tt>true</tt> if <tt>o</tt> adheres to the logic the 
         * <tt>JiperRandomQualifier</tt> checks, or <tt>false</tt> if not. 
         */
        boolean matches(Object o);
    }
    
    private static final String MIN_MAX = "min must be less than or equal to max";
    
    /* --- Singleton-related Methods --- */

    /**
     * Returns the single(ton) instance.
     * The first time this method is called, a new instance is created with the
     * default seed (current time in millis).
     */
    public static synchronized JiperRandom instance() {
        if (instance == null) {
            instance = new JiperRandom();
        }

        return instance;
    }

    /**
     * Returns the single(ton) instance.
     * The first time this method is called, a new instance is created with the
     * given seed, consecutive calls return the existing instance set with the
     * given seed.
     */
    public static synchronized JiperRandom instance(long seed) {
        if (instance == null) {
            instance = new JiperRandom(seed);
        } else {
            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);
        }

        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);
        }

        return (short) (min + nextShort((short) (max - min + 1)));
    }


    /* --- Integer-related Methods --- */

    /**
     * Randomizes an <tt>int</tt> value until it matches the provided 
     * <code>JiperRandomQualifier</code>. <b>NOTE</b>: The qualifier is 
     * provided an <code>Integer</code> with the randomized value as the 
     * <code>Object</code> to match.
     * 
     * @param qualifier The <code>JiperRandomQualifier</code> which qualifies
     *                     the randomized values.
     * @return A random integer which matches the 
     * <code>JiperRandomQualifier</code>.
     */
    public int nextInt(JiperRandomQualifier qualifier) {
        int returnValue;
        do {
            returnValue = nextInt();
        } while (! qualifier.matches(new Integer(returnValue)));
        
        return returnValue;
    }
    
    /**
     * Randomizes and <tt>int</tt> value in the given range, which answers the
     * specified qualifier.
     * 
     * @param min            The minimum value of the integer returned. 
     * @param max            The maximum value of the integer returned.
     * @param qualifier        The qualifier the returned integer must match.
     * @return an <tt>int</tt> in the specified range which matches the
     * specified qualifier.
     */
    public int nextInt(int min, int max, JiperRandomQualifier qualifier) {
        int returnValue;
        do {
            returnValue = nextInt(min, max);
        } while (! (qualifier.matches(new Integer(returnValue))));
        
        return returnValue;
    }
    
    /**
     * 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);
        }

        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);
        }

        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);
        }

        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();
    }
    
    /**
     * Picks a random element, which matches the provided 
     * <code>JiperRandomQualifier</code>, from the given 
     * <code>Collection</code>.
     * 
     * @param c            The <code>Collection</code> to pick an element from.
     * @param qualifier    The <code>Qualifier</code> the picked element has to
     *                     match.
     * 
     * @return            A random, qualified element from the
     *                     <code>Collection</code>.
     */
    public <T> T pickRandom(Collection<T> c, JiperRandomQualifier qualifier) {
        T returnValue = null;
        do {
            int elementIndex = super.nextInt(c.size());

            Iterator<T> iter = c.iterator();
            for (int i = 0; i < elementIndex; ++i) {
                iter.next();
            }
        
            returnValue = iter.next();
        } while (! qualifier.matches(returnValue));
        
        return returnValue;
    }
    
    /* --- Array-related Methods --- */

    /**
     * Picks a random element from the given array.
     */
    public <T> T pickRandom(T[] o) {
        return pickRandom(Arrays.asList(o));
    }
    
    /**
     * Picks a random element, which matches the provided 
     * <code>JiperRandomQualifier</code>, from the given array.
     * 
     * @param o                The array to pick an element from.
     * @param qualifier        The <code>Qualifier</code> the picked element has 
     *                         to match.
     * 
     * @return                A random, qualified element from the 
     *                         <code>Collection</code>.
     */
    public <T> T pickRandom(T[] o, JiperRandomQualifier qualifier) {
        return pickRandom(Arrays.asList(o), qualifier);
    }

    
    /* --- 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 <code>String</code> that adheres to the given 
     * {@link JiperRandomQualifier.util.JiperRandomQualifier}
     *
     * @param length     The requested length of the string. 
     * @param printable  Whether or not, the string should contain only
     *                   printable characters.
     * @param qualifier  The <code>JiperRandomQualifier</code> the return value
     *                      must adhere to
     * @return             A random value that adheres to <code>qualifier</code>
     */ 
    public String nextString
        (int length, boolean printable, JiperRandomQualifier qualifier) {
        String returnVal;
        
        do {
            returnVal = nextString(length, printable);
        } while (!qualifier.matches(returnVal));
        
        return returnVal;
    }
    
    /**
     * 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 valid XML Element name.
     *
     * @param length    The requested length of the string.
     * @param qualifier  The <code>JiperRandomQualifier</code> the return value
     *                      must adhere to
     */
    public String nextXmlString(int length, JiperRandomQualifier qualifier) {
        String returnVal;
        
        do {
            returnVal = nextXmlString(length);
        } while (!qualifier.matches(returnVal));
        
        return returnVal;
    }

    /**
     * Randomize a printeable <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> that adheres to the given 
     * {@link JiperRandomQualifier.util.JiperRandomQualifier}
     *
     * @param length     The requested length of the string. 
     * @param qualifier  The <code>JiperRandomQualifier</code> the return value
     *                      must adhere to
     * @return             A random value that adheres to <code>qualifier</code>
     */ 
    public String nextString(int length, JiperRandomQualifier qualifier) {
        return nextString(length, true, qualifier);
    }
    

    /**
     * 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 <code>String</code> that adheres to the given 
     * {@link JiperRandomQualifier.util.JiperRandomQualifier} of a length in the 
     * given range [min, max]
     *
     * @param length     The requested length of the string. 
     * @param printable  Whether or not, the string should contain only
     *                   printable characters.
     * @param qualifier  The <code>JiperRandomQualifier</code> the return value
     *                      must adhere to
     * @return             A random value that adheres to <code>qualifier</code>
     */ 
    public String nextString
        (int min, int max, boolean printable, JiperRandomQualifier qualifier) {
        String returnVal;
        
        do {
            returnVal = nextString(min, max, printable);
        } while (!qualifier.matches(returnVal));
        
        return returnVal;
    }

    /**
     * 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);
    }
    
    /**
     * Randomize a <code>String</code> that adheres to the given 
     * {@link JiperRandomQualifier.util.JiperRandomQualifier} of a length in the 
     * given range [min, max]
     *
     * @param length     The requested length of the string. 
     * @param qualifier  The <code>JiperRandomQualifier</code> the return value
     *                      must adhere to
     * @return             A random value that adheres to <code>qualifier</code>
     */ 
    public String nextString(int min, int max, JiperRandomQualifier qualifier) {
        return nextString(min, max, true, qualifier);
    }

    /* --- 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.
     */
    @SuppressWarnings("unchecked")
    public <T extends Enum> T nextEnum(Class<T> enumClass) {
        if (enumClass == null) {
            return null;
        }
        
        return pickRandom(enumClass.getEnumConstants());
    }

    /* --- Private Methods and Fields --- */

    /**
     * Constructs the object.
     */
    private JiperRandom() {
    }

    /**
     * Constructs the object with the given random seed.
     */
    private JiperRandom(long seed) {
        super(seed);
    }

    /** The single(ton) instance. */
    private static JiperRandom instance;

    /** The seed that was last set. */
    private long seed;

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


import java.math.BigInteger;
import java.util.*;

/**
* Jiper’s Random class adds utility methods in an attempt to
* corrects Sun’s laziness, and produce a better solution.


* This class also corrects (what I believe to be) an historical mistake, and
* is a singleton class..


* Useful methods include:
*


    *
  • nextXXX() for the byte and short types.
    *
  • nextXXX(mod) for all types.
    *
  • nextXXX(min,max) for all types.
    *
  • pickRandom() – picks a random element from a given
    * List or Set.
    *

*
* @see java.util.Random
*/
public final class JiperRandom extends java.util.Random {

/**
* The JiperRandomQualifier defines how to further qualify a value
* requested from {@link JiperRandom}.

*
* This class is awefully useful when you want a value is isn’t completely
* random, but adheres to some more application-specific logic (for example:
* a value that isn’t present in a database, a palyndrom, a value with some
* specific hashCode(), etc.)
*
* @author Allon M.
*/
public interface JiperRandomQualifier {

/**
* Checks if the parameter adheres to the logic the
* JiperRandomQualifier is supposed to check
* @param o The object to check (usually, the random value generated by
* {@link JiperRandom}
* @return true if o adheres to the logic the
* JiperRandomQualifier checks, or false if not.
*/
boolean matches(Object o);
}

private static final String MIN_MAX = “min must be less than or equal to max”;

/* — Singleton-related Methods — */

/**
* Returns the single(ton) instance.
* The first time this method is called, a new instance is created with the
* default seed (current time in millis).
*/
public static synchronized JiperRandom instance() {
if (instance == null) {
instance = new JiperRandom();
}

return instance;
}

/**
* Returns the single(ton) instance.
* The first time this method is called, a new instance is created with the
* given seed, consecutive calls return the existing instance set with the
* given seed.
*/
public static synchronized JiperRandom instance(long seed) {
if (instance == null) {
instance = new JiperRandom(seed);
} else {
instance.setSeed(seed);
}

return instance;
}

/* — Seed-related Methods — */

/**
* The last seed is saved, so it is possible to
* {@link #getSeed()} it later. Since java.util.Random‘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 byte value.
*/
public byte nextByte() {
return (byte) super.nextInt();
}

/**
* Randomize a byte value between 0 (inclusive) and the specified
* value (exclusive).
*/
public byte nextByte(byte b) {
return (byte) super.nextInt(b);
}

/**
* Randomize a byte value in the given range [min, max].
*/
public byte nextByte(byte min, byte max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX);
}

return (byte) (min + nextByte((byte) (max – min + 1)));
}

/* — Short-related Methods — */

/**
* Randomizes a short value.
*/
public short nextShort() {
return (short) super.nextInt();
}

/**
* Randomize a short value between 0 (inclusive) and the specified
* value (exclusive).
*/
public short nextShort(short s) {
return (short) super.nextInt(s);
}

/**
* Randomize a short value in the given range [min, max].
*/
public short nextShort(short min, short max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX);
}

return (short) (min + nextShort((short) (max – min + 1)));
}

/* — Integer-related Methods — */

/**
* Randomizes an int value until it matches the provided
* JiperRandomQualifier. NOTE: The qualifier is
* provided an Integer with the randomized value as the
* Object to match.
*
* @param qualifier The JiperRandomQualifier which qualifies
* the randomized values.
* @return A random integer which matches the
* JiperRandomQualifier.
*/
public int nextInt(JiperRandomQualifier qualifier) {
int returnValue;
do {
returnValue = nextInt();
} while (! qualifier.matches(new Integer(returnValue)));

return returnValue;
}

/**
* Randomizes and int value in the given range, which answers the
* specified qualifier.
*
* @param min The minimum value of the integer returned.
* @param max The maximum value of the integer returned.
* @param qualifier The qualifier the returned integer must match.
* @return an int in the specified range which matches the
* specified qualifier.
*/
public int nextInt(int min, int max, JiperRandomQualifier qualifier) {
int returnValue;
do {
returnValue = nextInt(min, max);
} while (! (qualifier.matches(new Integer(returnValue))));

return returnValue;
}

/**
* Randomize an int value in the given range [min, max].
*/
public int nextInt(int min, int max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX);
}

return (min + super.nextInt(max – min + 1));
}

/* — Long-related Methods — */

/**
* Randomize a long 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 long value in the given range [min, max].
*/
public long nextLong(long min, long max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX);
}

return (min + nextLong(max – min + 1));
}

/* — Float-related Methods — */

/**
* Randomize a float value between 0.0 (inclusive) and the
* specified value (exclusive).
*/
public float nextFloat(float f) {
return nextFloat(f, false);
}

/**
* Randomize a float 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 float value in the given range [min, max].
*/
public float nextFloat(float min, float max) {
if (min > max) {
throw new IllegalArgumentException(
MIN_MAX);
}

return (min + nextFloat(max – min, true));
}

/* — Double-related Methods — */

/**
* Randomize a double value between 0.0 (inclusive) and the
* specified value (exclusive).
*/
public double nextDouble(double d) {
return nextDouble(d, false);
}

/**
* Randomize a double 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 Collection.
*/
public T pickRandom(Collection c) {
int elementIndex = super.nextInt(c.size());

Iterator iter = c.iterator();
for (int i = 0; i < elementIndex; ++i) {
iter.next();
}

return iter.next();
}

/**
* Picks a random element, which matches the provided
* JiperRandomQualifier, from the given
* Collection.
*
* @param c The Collection to pick an element from.
* @param qualifier The Qualifier the picked element has to
* match.
*
* @return A random, qualified element from the
* Collection.
*/
public T pickRandom(Collection c, JiperRandomQualifier qualifier) {
T returnValue = null;
do {
int elementIndex = super.nextInt(c.size());

Iterator iter = c.iterator();
for (int i = 0; i < elementIndex; ++i) {
iter.next();
}

returnValue = iter.next();
} while (! qualifier.matches(returnValue));

return returnValue;
}

/* — Array-related Methods — */

/**
* Picks a random element from the given array.
*/
public T pickRandom(T[] o) {
return pickRandom(Arrays.asList(o));
}

/**
* Picks a random element, which matches the provided
* JiperRandomQualifier, from the given array.
*
* @param o The array to pick an element from.
* @param qualifier The Qualifier the picked element has
* to match.
*
* @return A random, qualified element from the
* Collection.
*/
public T pickRandom(T[] o, JiperRandomQualifier qualifier) {
return pickRandom(Arrays.asList(o), qualifier);
}

/* — String-related Methods — */

/**
* Randomize a String.
*
* @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 String that adheres to the given
* {@link JiperRandomQualifier.util.JiperRandomQualifier}
*
* @param length The requested length of the string.
* @param printable Whether or not, the string should contain only
* printable characters.
* @param qualifier The JiperRandomQualifier the return value
* must adhere to
* @return A random value that adheres to qualifier
*/
public String nextString
(int length, boolean printable, JiperRandomQualifier qualifier) {
String returnVal;

do {
returnVal = nextString(length, printable);
} while (!qualifier.matches(returnVal));

return returnVal;
}

/**
* 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 valid XML Element name.
*
* @param length The requested length of the string.
* @param qualifier The JiperRandomQualifier the return value
* must adhere to
*/
public String nextXmlString(int length, JiperRandomQualifier qualifier) {
String returnVal;

do {
returnVal = nextXmlString(length);
} while (!qualifier.matches(returnVal));

return returnVal;
}

/**
* Randomize a printeable String.
*
* @param length The requested length of the string.
*/
public String nextString(int length) {
return nextString(length, true);
}

/**
* Randomize a String that adheres to the given
* {@link JiperRandomQualifier.util.JiperRandomQualifier}
*
* @param length The requested length of the string.
* @param qualifier The JiperRandomQualifier the return value
* must adhere to
* @return A random value that adheres to qualifier
*/
public String nextString(int length, JiperRandomQualifier qualifier) {
return nextString(length, true, qualifier);
}

/**
* Randomize a String 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 String that adheres to the given
* {@link JiperRandomQualifier.util.JiperRandomQualifier} of a length in the
* given range [min, max]
*
* @param length The requested length of the string.
* @param printable Whether or not, the string should contain only
* printable characters.
* @param qualifier The JiperRandomQualifier the return value
* must adhere to
* @return A random value that adheres to qualifier
*/
public String nextString
(int min, int max, boolean printable, JiperRandomQualifier qualifier) {
String returnVal;

do {
returnVal = nextString(min, max, printable);
} while (!qualifier.matches(returnVal));

return returnVal;
}

/**
* Randomize a printable String of a length in the
* given range [min, max].
*/
public String nextString(int min, int max) {
return nextString(nextInt(min, max), true);
}

/**
* Randomize a String that adheres to the given
* {@link JiperRandomQualifier.util.JiperRandomQualifier} of a length in the
* given range [min, max]
*
* @param length The requested length of the string.
* @param qualifier The JiperRandomQualifier the return value
* must adhere to
* @return A random value that adheres to qualifier
*/
public String nextString(int min, int max, JiperRandomQualifier qualifier) {
return nextString(min, max, true, qualifier);
}

/* — General Utility Methods — */

/**
* Creates a byte 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 The enum type.
* @param enumClass The enum class to randomize.
*
* @return A random enum from the given enum, or null if got null.
*/
@SuppressWarnings(“unchecked”)
public T nextEnum(Class enumClass) {
if (enumClass == null) {
return null;
}

return pickRandom(enumClass.getEnumConstants());
}

/* — Private Methods and Fields — */

/**
* Constructs the object.
*/
private JiperRandom() {
}

/**
* Constructs the object with the given random seed.
*/
private JiperRandom(long seed) {
super(seed);
}

/** The single(ton) instance. */
private static JiperRandom instance;

/** The seed that was last set. */
private long seed;

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

Advertisements
This entry was posted in Java and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s