RegexStoryParser.java
- package org.jbehave.core.parsers;
- import static java.util.Arrays.asList;
- import static java.util.regex.Pattern.DOTALL;
- import static java.util.regex.Pattern.compile;
- import static org.apache.commons.lang3.StringUtils.removeStart;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.apache.commons.lang3.StringUtils;
- import org.jbehave.core.annotations.AfterScenario.Outcome;
- import org.jbehave.core.annotations.Scope;
- import org.jbehave.core.io.LoadFromClasspath;
- import org.jbehave.core.io.ResourceLoader;
- import org.jbehave.core.model.Description;
- import org.jbehave.core.model.ExamplesTable;
- import org.jbehave.core.model.ExamplesTableFactory;
- import org.jbehave.core.model.GivenStories;
- import org.jbehave.core.model.Lifecycle;
- import org.jbehave.core.model.Lifecycle.Steps;
- import org.jbehave.core.model.Meta;
- import org.jbehave.core.model.Narrative;
- import org.jbehave.core.model.Scenario;
- import org.jbehave.core.model.Story;
- import org.jbehave.core.model.TableTransformers;
- /**
- * Pattern-based story parser, which uses the keywords provided to parse the
- * textual story into a {@link Story}.
- */
- public class RegexStoryParser extends AbstractRegexParser implements StoryParser {
- public static final ResourceLoader DEFAULT_RESOURCE_LOADER = new LoadFromClasspath();
- public static final TableTransformers DEFAULT_TABLE_TRANSFORMERS = new TableTransformers();
- private final ExamplesTableFactory tableFactory;
- public RegexStoryParser() {
- this(new ExamplesTableFactory(DEFAULT_RESOURCE_LOADER, DEFAULT_TABLE_TRANSFORMERS));
- }
- public RegexStoryParser(ExamplesTableFactory tableFactory) {
- super(tableFactory.keywords());
- this.tableFactory = tableFactory;
- }
- @Override
- public Story parseStory(String storyAsText) {
- return parseStory(storyAsText, null);
- }
- @Override
- public Story parseStory(String storyAsText, String storyPath) {
- Description description = parseDescriptionFrom(storyAsText);
- Meta meta = parseStoryMetaFrom(storyAsText);
- Narrative narrative = parseNarrativeFrom(storyAsText);
- GivenStories givenStories = parseGivenStories(storyAsText);
- Lifecycle lifecycle = parseLifecycle(storyAsText);
- if (lifecycle != null) {
- ExamplesTable storyExamplesTable = lifecycle.getExamplesTable();
- if (!storyExamplesTable.isEmpty()) {
- useExamplesTableForGivenStories(givenStories, storyExamplesTable);
- }
- }
- List<Scenario> scenarios = parseScenariosFrom(storyAsText);
- return new Story(storyPath, description, meta, narrative, givenStories, lifecycle, scenarios);
- }
- private Description parseDescriptionFrom(String storyAsText) {
- Matcher findingDescription = findingDescription().matcher(storyAsText);
- if (findingDescription.matches()) {
- return new Description(findingDescription.group(1).trim());
- }
- return Description.EMPTY;
- }
- private Meta parseStoryMetaFrom(String storyAsText) {
- Matcher findingMeta = findingStoryMeta().matcher(preScenarioText(storyAsText));
- if (findingMeta.matches()) {
- String meta = findingMeta.group(1).trim();
- return Meta.createMeta(meta, keywords());
- }
- return Meta.EMPTY;
- }
- private String preScenarioText(String storyAsText) {
- String[] split = storyAsText.split(keywords().scenario());
- return split.length > 0 ? split[0] : storyAsText;
- }
- private Narrative parseNarrativeFrom(String storyAsText) {
- Matcher findingNarrative = findingNarrative().matcher(storyAsText);
- if (findingNarrative.matches()) {
- String narrative = findingNarrative.group(1).trim();
- return createNarrative(narrative);
- }
- return Narrative.EMPTY;
- }
- @SuppressWarnings("checkstyle:LocalVariableName")
- private Narrative createNarrative(String narrative) {
- Matcher findingElements = findingNarrativeElements().matcher(narrative);
- if (findingElements.matches()) {
- String inOrderTo = findingElements.group(1).trim();
- String asA = findingElements.group(2).trim();
- String iWantTo = findingElements.group(3).trim();
- return new Narrative(inOrderTo, asA, iWantTo);
- }
- Matcher findingAlternativeElements = findingAlternativeNarrativeElements().matcher(narrative);
- if (findingAlternativeElements.matches()) {
- String asA = findingAlternativeElements.group(1).trim();
- String iWantTo = findingAlternativeElements.group(2).trim();
- String soThat = findingAlternativeElements.group(3).trim();
- return new Narrative("", asA, iWantTo, soThat);
- }
- return Narrative.EMPTY;
- }
-
- private GivenStories parseGivenStories(String storyAsText) {
- String scenarioKeyword = keywords().scenario();
- // use text before scenario keyword, if found
- String beforeScenario = "";
- if (StringUtils.contains(storyAsText, scenarioKeyword)) {
- beforeScenario = StringUtils.substringBefore(storyAsText, scenarioKeyword);
- }
- Matcher findingGivenStories = findingStoryGivenStories().matcher(beforeScenario);
- String givenStories = findingGivenStories.find() ? findingGivenStories.group(1).trim() : NONE;
- return new GivenStories(givenStories);
- }
- private Lifecycle parseLifecycle(String storyAsText) {
- String scenarioKeyword = keywords().scenario();
- // use text before scenario keyword, if found
- String beforeScenario = "";
- if (StringUtils.contains(storyAsText, scenarioKeyword)) {
- beforeScenario = StringUtils.substringBefore(storyAsText, scenarioKeyword);
- }
- Matcher findingLifecycle = findingLifecycle().matcher(beforeScenario);
- String lifecycle;
- ExamplesTable examplesTable;
- if (findingLifecycle.find()) {
- lifecycle = findingLifecycle.group(1).trim();
- String examplesTableAsString = findExamplesTable(findingLifecycle.group(0));
- examplesTable = parseExamplesTable(examplesTableAsString);
- } else {
- lifecycle = NONE;
- examplesTable = ExamplesTable.EMPTY;
- }
- Matcher findingBeforeAndAfter = compile(
- ".*" + keywords().before() + "(.*)\\s*" + keywords().after() + "(.*)\\s*", DOTALL).matcher(lifecycle);
- if (findingBeforeAndAfter.matches()) {
- String beforeLifecycle = findingBeforeAndAfter.group(1).trim();
- List<Steps> beforeSteps = parseBeforeLifecycle(beforeLifecycle);
- String afterLifecycle = findingBeforeAndAfter.group(2).trim();
- List<Steps> afterSteps = parseAfterLifecycle(afterLifecycle);
- return new Lifecycle(examplesTable, beforeSteps, afterSteps);
- }
- Matcher findingBefore = compile(".*" + keywords().before() + "(.*)\\s*", DOTALL).matcher(lifecycle);
- if (findingBefore.matches()) {
- String beforeLifecycle = findingBefore.group(1).trim();
- List<Steps> beforeSteps = parseBeforeLifecycle(beforeLifecycle);
- return new Lifecycle(examplesTable, beforeSteps, Arrays.<Steps>asList());
- }
- Matcher findingAfter = compile(".*" + keywords().after() + "(.*)\\s*", DOTALL).matcher(lifecycle);
- if (findingAfter.matches()) {
- List<Steps> beforeSteps = asList();
- String afterLifecycle = findingAfter.group(1).trim();
- List<Steps> afterSteps = parseAfterLifecycle(afterLifecycle);
- return new Lifecycle(examplesTable, beforeSteps, afterSteps);
- }
- return new Lifecycle(examplesTable);
- }
- private List<Steps> parseBeforeLifecycle(String lifecycleAsText) {
- List<Steps> list = new ArrayList<>();
- for (String byScope : lifecycleAsText.split(keywords().scope())) {
- byScope = byScope.trim();
- if (byScope.isEmpty()) {
- continue;
- }
- Scope scope = parseScope(findScope(keywords().scope() + byScope));
- Steps steps = new Steps(scope, findSteps(startingWithNL(byScope)));
- list.add(steps);
- }
- return list;
- }
- private List<Steps> parseAfterLifecycle(String lifecycleAsText) {
- List<Steps> list = new ArrayList<>();
- for (String byScope : lifecycleAsText.split(keywords().scope())) {
- byScope = byScope.trim();
- if (byScope.isEmpty()) {
- continue;
- }
- Scope scope = parseScope(findScope(keywords().scope() + byScope));
- for (String byOutcome : byScope.split(keywords().outcome())) {
- byOutcome = byOutcome.trim();
- if (byOutcome.isEmpty()) {
- continue;
- }
- String outcomeAsText = findOutcome(byOutcome);
- String filtersAsText = findFilters(removeStart(byOutcome, outcomeAsText));
- List<String> steps = findSteps(startingWithNL(removeStart(byOutcome, filtersAsText)));
- list.add(new Steps(scope, parseOutcome(outcomeAsText), parseFilters(filtersAsText), steps));
- }
- }
- return list;
- }
- private String findScope(String lifecycleAsText) {
- Matcher findingScope = findingLifecycleScope().matcher(lifecycleAsText.trim());
- if (findingScope.matches()) {
- return findingScope.group(1).trim();
- }
- return NONE;
- }
- private Scope parseScope(String scopeAsText) {
- if (scopeAsText.trim().equals(keywords().scopeStep())) {
- return Scope.STEP;
- } else if (scopeAsText.trim().equals(keywords().scopeScenario())) {
- return Scope.SCENARIO;
- } else if (scopeAsText.trim().equals(keywords().scopeStory())) {
- return Scope.STORY;
- }
- return Scope.SCENARIO;
- }
- private String findOutcome(String stepsByOutcome) {
- Matcher findingOutcome = findingLifecycleOutcome().matcher(stepsByOutcome);
- if (findingOutcome.matches()) {
- return findingOutcome.group(1).trim();
- }
- return keywords().outcomeAny();
- }
- private Outcome parseOutcome(String outcomeAsText) {
- if (outcomeAsText.equals(keywords().outcomeSuccess())) {
- return Outcome.SUCCESS;
- } else if (outcomeAsText.equals(keywords().outcomeFailure())) {
- return Outcome.FAILURE;
- }
- return Outcome.ANY;
- }
- private String findFilters(String stepsByFilters) {
- Matcher findingFilters = findingLifecycleFilters().matcher(stepsByFilters.trim());
- if (findingFilters.matches()) {
- return findingFilters.group(1).trim();
- }
- return NONE;
- }
- private String parseFilters(String filtersAsText) {
- return removeStart(filtersAsText, keywords().metaFilter()).trim();
- }
- private List<Scenario> parseScenariosFrom(String storyAsText) {
- List<Scenario> parsed = new ArrayList<>();
- for (String scenarioAsText : splitScenarios(storyAsText)) {
- parsed.add(parseScenario(scenarioAsText));
- }
- return parsed;
- }
- private List<String> splitScenarios(String storyAsText) {
- String scenarioKeyword = keywords().scenario();
- // use text after scenario keyword, if found
- if (StringUtils.contains(storyAsText, scenarioKeyword)) {
- storyAsText = StringUtils.substringAfter(storyAsText, scenarioKeyword);
- }
- return splitElements(storyAsText, scenarioKeyword);
- }
- private Scenario parseScenario(String scenarioAsText) {
- String title = findScenarioTitle(scenarioAsText);
- String scenarioWithoutKeyword = removeStart(scenarioAsText, keywords().scenario()).trim();
- String scenarioWithoutTitle = removeStart(scenarioWithoutKeyword, title);
- scenarioWithoutTitle = startingWithNL(scenarioWithoutTitle);
- Meta meta = findScenarioMeta(scenarioWithoutTitle);
- String examplesTableAsString = findExamplesTable(scenarioWithoutTitle);
- ExamplesTable examplesTable = parseExamplesTable(examplesTableAsString);
- GivenStories givenStories = findScenarioGivenStories(scenarioWithoutTitle);
- useExamplesTableForGivenStories(givenStories, examplesTable);
- List<String> rawSteps = new ArrayList<>();
- if (examplesTableAsString.trim().isEmpty()) {
- rawSteps.addAll(findSteps(scenarioWithoutTitle));
- } else {
- int afterExampleIndex = scenarioWithoutTitle.indexOf(examplesTableAsString)
- + examplesTableAsString.length();
- rawSteps.addAll(findSteps(scenarioWithoutTitle.substring(0, afterExampleIndex)));
- }
- return new Scenario(title, meta, givenStories, examplesTable, rawSteps);
- }
- private void useExamplesTableForGivenStories(GivenStories givenStories, ExamplesTable examplesTable) {
- if (givenStories.requireParameters()) {
- givenStories.useExamplesTable(examplesTable);
- }
- }
- private String findScenarioTitle(String scenarioAsText) {
- Matcher findingTitle = findingScenarioTitle().matcher(scenarioAsText);
- return findingTitle.find() ? findingTitle.group(1).trim() : NONE;
- }
- private Meta findScenarioMeta(String scenarioAsText) {
- Matcher findingMeta = findingScenarioMeta().matcher(scenarioAsText);
- if (findingMeta.matches()) {
- String meta = findingMeta.group(1).trim();
- return Meta.createMeta(meta, keywords());
- }
- return Meta.EMPTY;
- }
- private String findExamplesTable(String scenarioAsText) {
- Matcher findingTable = findingExamplesTable().matcher(scenarioAsText);
- return findingTable.find() ? findingTable.group(1).trim() : NONE;
- }
- private ExamplesTable parseExamplesTable(String tableInput) {
- return tableFactory.createExamplesTable(tableInput);
- }
- private GivenStories findScenarioGivenStories(String scenarioAsText) {
- Matcher findingGivenStories = findingScenarioGivenStories().matcher(scenarioAsText);
- String givenStories = findingGivenStories.find() ? findingGivenStories.group(1).trim() : NONE;
- return new GivenStories(givenStories);
- }
- // Regex Patterns
- private Pattern findingDescription() {
- String metaOrNarrativeOrLifecycleOrScenario = concatenateWithOr(keywords().meta(), keywords().narrative(),
- keywords().lifecycle(), keywords().scenario());
- return compile("(.*?)(" + metaOrNarrativeOrLifecycleOrScenario + ").*", DOTALL);
- }
- private Pattern findingStoryMeta() {
- String narrativeOrLifecycleOrGivenStories = concatenateWithOr(keywords().narrative(), keywords().lifecycle(),
- keywords().givenStories());
- return compile(".*" + keywords().meta() + "(.*?)\\s*(\\Z|" + narrativeOrLifecycleOrGivenStories + ").*",
- DOTALL);
- }
- private Pattern findingNarrative() {
- String givenStoriesOrLifecycleOrScenario = concatenateWithOr(keywords().givenStories(), keywords().lifecycle(),
- keywords().scenario());
- return compile(".*" + keywords().narrative() + "(.*?)\\s*(" + givenStoriesOrLifecycleOrScenario + ").*",
- DOTALL);
- }
- private Pattern findingNarrativeElements() {
- return compile(".*" + keywords().inOrderTo() + "(.*)\\s*" + keywords().asA() + "(.*)\\s*" + keywords().iWantTo()
- + "(.*)", DOTALL);
- }
- private Pattern findingAlternativeNarrativeElements() {
- return compile(
- ".*" + keywords().asA() + "(.*)\\s*" + keywords().iWantTo() + "(.*)\\s*" + keywords().soThat() + "(.*)",
- DOTALL);
- }
-
- private Pattern findingStoryGivenStories() {
- String lifecycleOrScenario = concatenateWithOr(keywords().lifecycle(), keywords().scenario());
- return compile(".*" + keywords().givenStories() + "(.*?)\\s*(\\Z|" + lifecycleOrScenario + ").*", DOTALL);
- }
-
- private Pattern findingLifecycle() {
- return compile(".*" + keywords().lifecycle() + "\\s*(.*)", DOTALL);
- }
- private Pattern findingLifecycleScope() {
- String startingWords = concatenateStartingWords();
- return compile(keywords().scope() + "((.)*?)\\s*(" + keywords().outcome() + "|" + keywords().metaFilter() + "|"
- + startingWords + ").*", DOTALL);
- }
- private Pattern findingLifecycleOutcome() {
- String startingWords = concatenateStartingWords();
- String outcomes = concatenateWithOr(keywords().outcomeAny(), keywords().outcomeSuccess(),
- keywords().outcomeFailure());
- return compile("\\s*(" + outcomes + ")\\s*(" + keywords().metaFilter() + "|" + startingWords + ").*", DOTALL);
- }
- private Pattern findingLifecycleFilters() {
- String startingWords = concatenateStartingWords();
- String filters = concatenateWithOr(keywords().metaFilter());
- return compile("\\s*(" + filters + "[\\w\\+\\-\\_\\s]*)(" + startingWords + ").*", DOTALL);
- }
- private Pattern findingScenarioTitle() {
- String startingWords = concatenateStartingWords();
- return compile(keywords().scenario() + "(.*?)\\s*(" + keywords().meta() + "|" + keywords().givenStories() + "|"
- + startingWords + "|$).*", DOTALL);
- }
- private Pattern findingScenarioMeta() {
- String startingWords = concatenateStartingWords();
- return compile(
- ".*" + keywords().meta() + "(.*?)\\s*(" + keywords().givenStories() + "|" + startingWords + "|$).*",
- DOTALL);
- }
- private Pattern findingScenarioGivenStories() {
- String startingWords = concatenateStartingWords();
- return compile("\\n" + keywords().givenStories() + "((.|\\n)*?)\\s*(" + startingWords + ").*", DOTALL);
- }
- private Pattern findingExamplesTable() {
- return compile("\\n" + keywords().examplesTable() + "\\s*(.*?)(?:\\n" + keywords().ignorable() + ".*)?$",
- DOTALL);
- }
- private String concatenateWithOr(String... keywords) {
- return concatenateWithOr(NONE, asList(keywords));
- }
- }