diff --git a/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java new file mode 100644 index 0000000000..8fd7d28628 --- /dev/null +++ b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java @@ -0,0 +1,114 @@ +/** + * 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 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; + 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 as string + */ + 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 generated regexp + */ + 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); + } + + public Random getRandom() { + return random; + } + + public void setRandom(Random random) { + this.random = random; + } + + /** + * 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; + } +} \ No newline at end of file diff --git a/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java new file mode 100644 index 0000000000..f60e4bf5db --- /dev/null +++ b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java @@ -0,0 +1,64 @@ +/** + * 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 org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class XegerTest { + + @Test + public void shouldGenerateTextCorrectly() { + String regex = "[ab]{4,6}c"; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.matches(regex)); + } + } + + @Test + public void testRepeatableRegex() { + for (int x = 0; x < 1000; x++) { + Xeger generator = new Xeger("[ab]{4,6}c", new Random(1000)); + Xeger generator2 = new Xeger("[ab]{4,6}c", new Random(1000)); + + List firstRegexList = generateRegex(generator, 100); + List secondRegexList = generateRegex(generator2, 100); + + for (int i = 0; i < firstRegexList.size(); i++) { + assertEquals("Index mismatch: " + i, firstRegexList.get(i), + secondRegexList.get(i)); + } + } + } + + private List generateRegex(Xeger generator, int count) { + List regexList = new ArrayList(); + for (int i = 0; i < count; i++) { + regexList.add(generator.generate()); + } + return regexList; + } +} \ No newline at end of file diff --git a/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java new file mode 100644 index 0000000000..ccabfeb54d --- /dev/null +++ b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java @@ -0,0 +1,39 @@ +/** + * 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 org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Random; + +import static org.junit.Assert.assertThat; + +public class XegerUtilsTest { + + @Test + public void shouldGenerateRandomNumberCorrectly() { + Random random = new Random(); + for (int i = 0; i < 100; i++) { + int number = Xeger.getRandomInt(3, 7, random); + assertThat(number, Matchers.greaterThanOrEqualTo(3)); + assertThat(number, Matchers.lessThanOrEqualTo(7)); + } + } + +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 783681eebd..8ada1b5a2a 100644 --- a/build.gradle +++ b/build.gradle @@ -47,9 +47,6 @@ subprojects { repositories { mavenLocal() mavenCentral() - maven { - url "https://jitpack.io" - } } //Dependencies in all subprojects - http://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/ @@ -101,8 +98,9 @@ project(':accurest-converters') { compile project(':accurest-core') compile 'org.apache.commons:commons-lang3:3.3.2' compile 'commons-io:commons-io:[2.4,)' - compile 'com.github.marcingrzejszczak:xeger:20130128' + compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger testCompile 'com.github.tomakehurst:wiremock:1.53' + testCompile 'org.hamcrest:hamcrest-all:1.3' } }