nl.flotsam.xeger.Xeger Maven / Gradle / Ivy
/**
* Copyright 2009 Wilfred Springer
* Copyright 2012 Jason Pell
* Copyright 2013 Antonio García-Domínguez
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.flotsam.xeger;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.State;
import dk.brics.automaton.Transition;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Random;
/**
* An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression
* matcher: an instance of this class will produce text that is guaranteed to match the regular expression passed in.
*/
public class Xeger {
private final Automaton automaton;
@Getter @Setter private Random random;
/**
* Constructs a new instance, accepting the regular expression and the randomizer.
*
* @param regex The regular expression. (Not null
.)
* @param random The object that will randomize the way the String is generated. (Not null
.)
* @throws IllegalArgumentException If the regular expression is invalid.
*/
public Xeger(String regex, Random random) {
assert regex != null;
assert random != null;
this.automaton = new RegExp(regex).toAutomaton();
this.random = random;
}
/**
* As {@link nl.flotsam.xeger.Xeger#Xeger(String, java.util.Random)}, creating a {@link java.util.Random} instance
* implicityly.
*
* @param regex 正则表达式
*/
public Xeger(String regex) {
this(regex, new Random());
}
/**
* Generates a random String that is guaranteed to match the regular expression passed to the constructor.
*
* @return random string
*/
public String generate() {
StringBuilder builder = new StringBuilder();
generate(builder, automaton.getInitialState());
return builder.toString();
}
private void generate(StringBuilder builder, State state) {
List transitions = state.getSortedTransitions(false);
if (transitions.size() == 0) {
assert state.isAccept();
return;
}
int nroptions = state.isAccept() ? transitions.size() : transitions.size() - 1;
int option = Xeger.getRandomInt(0, nroptions, random);
if (state.isAccept() && option == 0) { // 0 is considered stop
return;
}
// Moving on to next transition
Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0));
appendChoice(builder, transition);
generate(builder, transition.getDest());
}
private void appendChoice(StringBuilder builder, Transition transition) {
char c = (char) Xeger.getRandomInt(transition.getMin(), transition.getMax(), random);
builder.append(c);
}
/**
* Generates a random number within the given bounds.
*
* @param min The minimum number (inclusive).
* @param max The maximum number (inclusive).
* @param random The object used as the randomizer.
* @return A random number in the given range.
*/
static int getRandomInt(int min, int max, Random random) {
// Use random.nextInt as it guarantees a uniform distribution
int maxForRandom = max - min + 1;
return random.nextInt(maxForRandom) + min;
}
}