GreexGenerator.java
package com.navigamez.greex;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import java.util.Random;
import java.util.Set;
/**
* Generates matches for a given regular expression. Support for regular expressions is provided by
* {@link RegExp}.
* <p>
* To use this class, create a new generator instance with your regular expression, then use the
* generate methods to generate your matches. This example will use this regular expression:
* <p>
* <code>
* (white|black)|((light|dark) )?(red|green|blue|gray)
* </code>
* <p>
* To generate a random match, create an instance of {@link GreexGenerator} and use
* {@link #generateRandom}:
* <p>
* <code>
* GreexGenerator generator = new GreexGenerator("(white|black)|((light|dark) )?(red|green|blue|gray)");<br>
* String match = generator.generateRandom();<br>
* System.out.println(match); // e.g. "dark red"
* </code>
* <p>
* To generate all matches, create an instance of {@link GreexGenerator} and use
* {@link #generateAll}:
* <p>
* <code>
* GreexGenerator generator = new GreexGenerator("(white|black)|((light|dark) )?(red|green|blue|gray)");<br>
* List<String> matches = generator.generateAll();<br>
* System.out.println(matches.size()); // "14"
* </code>
* <p>
* When generating all matches for a non-finite regular expression, you must provide a max length or
* you will get a {@link StackOverflowError} or {@link OutOfMemoryError}. The max length defaults to
* {@link Integer#MAX_VALUE} when not provided, which is fine for finite regular expressions.
* <p>
* <b>Thread Safety</b>
* <p>
* This class is not 100% thread safe. Here is the thread safety profile of each method.
* <table>
* <tr>
* <td><b>Method</b></td>
* <td><b>Thread Safety</b></td>
* </tr>
* <tr>
* <td>{@link #generateAll()}</td>
* <td>Always thread safe</td>
* </tr>
* <tr>
* <td>{@link #generateAll(int)}</td>
* <td>Always thread safe</td>
* </tr>
* <tr>
* <td>{@link #generateRandom()}</td>
* <td><font color="red">Not thread safe*</font></td>
* </tr>
* <tr>
* <td>{@link #generateRandom(long)}</td>
* <td>Always thread safe</td>
* </tr>
* <tr>
* <td>{@link #generateRandom(Random)}</td>
* <td><i>Sometimes</i> thread safe**</td>
* </tr>
* </table>
* <p>
* * This method uses an internal {@link Random} with no synchronization, and so it is not thread
* safe.<br>
* ** This method is only thread safe if the calling class is managing the {@link Random} in a
* thread-safe way.
*
* @author Brian Saltz
* @since 1.0
*/
public class GreexGenerator {
private final Automaton automaton;
private final Random random = new Random();
/**
* Create a new generator using the given regular expression.
*
* @param regex the regular expression that will be used for match generation
*/
public GreexGenerator(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
this.automaton = new RegExp(regex).toAutomaton();
}
/**
* Generate all the matches for this generator's regular expression. This method is the same as
* invoking {@code generateAll(Integer.MAX_VALUE)}.
* <p>
* This method is always thread safe.
*
* @return an unordered set of all matches
* @throws OutOfMemoryError might be thrown if the regular expression is non-finite
* @throws StackOverflowError might be thrown if the regular expression is non-finite
*/
public Set<String> generateAll() throws StackOverflowError, OutOfMemoryError {
return generateAll(Integer.MAX_VALUE);
}
/**
* Generate all the matches for this generator's regular expression where the length of the
* generated string is less than the given maximum length.
* <p>
* This method is always thread safe.
*
* @param maxLength the maximum string length for generated matches
* @return an unordered set of all matches with lengths less than the given maximum length
* @throws OutOfMemoryError might be thrown if the regular expression is non-finite
* @throws StackOverflowError might be thrown if the regular expression is non-finite
*/
public Set<String> generateAll(int maxLength) throws StackOverflowError, OutOfMemoryError {
return GreexAllGenerator.generateAll(automaton, maxLength);
}
/**
* Generates a random match for this generator's regular expression. This uses an internal
* {@link Random} that was created when the instance was constructed. Subsequent calls continue
* to use the same {@link Random} instance.
* <p>
* This method is never thread safe.
*
* @return a random string that matches the given regular expression
* @throws StackOverflowError might be thrown if the regular expression is non-finite
*/
public String generateRandom() throws StackOverflowError {
return generateRandom(random);
}
/**
* Generates a random match for this generator's regular expression. This creates a new
* {@link Random} instance using the given {@code seed}. Subsequent calls with the same seed
* will create a new {@link Random} each time, and so this will always return the same result
* for the same regular expression and seed.
* <p>
* This method is always thread safe.
*
* @param seed the seed to use for the {@link Random} instance.
* @return a random string that matches the given regular expression
* @throws StackOverflowError might be thrown if the regular expression is non-finite
*/
public String generateRandom(long seed) throws StackOverflowError {
return generateRandom(new Random(seed));
}
/**
* Generates a random match for this generator's regular expression. This uses the given
* {@link Random} instance.
* <p>
* This method is only thread safe if the given {@link Random} is managed in a thread-safe way.
*
* @param random the {@link Random} to use for generation.
* @return a random string that matches the given regular expression
* @throws StackOverflowError might be thrown if the regular expression is non-finite
*/
public String generateRandom(Random random) throws StackOverflowError {
return GreexRandomGenerator.generateRandom(this.automaton, random);
}
}