Coverage Report - org.jbehave.core.parsers.RegexStoryParser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexStoryParser
100%
90/90
100%
34/34
1.87
 
 1  
 package org.jbehave.core.parsers;
 2  
 
 3  
 import static java.util.regex.Pattern.DOTALL;
 4  
 import static java.util.regex.Pattern.compile;
 5  
 
 6  
 import java.io.File;
 7  
 import java.util.ArrayList;
 8  
 import java.util.List;
 9  
 import java.util.regex.Matcher;
 10  
 import java.util.regex.Pattern;
 11  
 
 12  
 import org.apache.commons.lang.StringUtils;
 13  
 import org.jbehave.core.configuration.Keywords;
 14  
 import org.jbehave.core.i18n.LocalizedKeywords;
 15  
 import org.jbehave.core.model.Description;
 16  
 import org.jbehave.core.model.ExamplesTable;
 17  
 import org.jbehave.core.model.Narrative;
 18  
 import org.jbehave.core.model.Scenario;
 19  
 import org.jbehave.core.model.Story;
 20  
 
 21  
 /**
 22  
  * Pattern-based story parser, which uses the keywords provided to parse the
 23  
  * textual story into a {@link Story}.
 24  
  */
 25  
 public class RegexStoryParser implements StoryParser {
 26  
 
 27  
     private static final String NONE = "";
 28  
     private static final String COMMA = ",";
 29  
     private final Keywords keywords;
 30  
 
 31  
     public RegexStoryParser() {
 32  20
         this(new LocalizedKeywords());
 33  20
     }
 34  
 
 35  414
     public RegexStoryParser(Keywords keywords) {
 36  414
         this.keywords = keywords;
 37  414
     }
 38  
 
 39  
     public Story parseStory(String storyAsText) {
 40  1
         return parseStory(storyAsText, null);
 41  
     }
 42  
 
 43  
     public Story parseStory(String storyAsText, String storyPath) {
 44  16
         Description description = parseDescriptionFrom(storyAsText);
 45  16
         Narrative narrative = parseNarrativeFrom(storyAsText);
 46  16
         List<Scenario> scenarios = parseScenariosFrom(storyAsText);
 47  16
         Story story = new Story(storyPath, description, narrative, scenarios);
 48  16
         if ( storyPath != null ){
 49  12
             story.namedAs(new File(storyPath).getName());
 50  
         }
 51  16
         return story;
 52  
     }
 53  
 
 54  
     private Description parseDescriptionFrom(String storyAsText) {
 55  16
         Matcher findingDescription = patternToPullDescriptionIntoGroupOne().matcher(storyAsText);
 56  16
         if (findingDescription.matches()) {
 57  8
             return new Description(findingDescription.group(1).trim());
 58  
         }
 59  8
         return Description.EMPTY;
 60  
     }
 61  
 
 62  
     private Narrative parseNarrativeFrom(String storyAsText) {
 63  16
         Matcher findingNarrative = patternToPullNarrativeIntoGroupOne().matcher(storyAsText);
 64  16
         if (findingNarrative.matches()) {
 65  3
             String narrative = findingNarrative.group(1).trim();
 66  3
             return createNarrative(narrative);
 67  
         }
 68  13
         return Narrative.EMPTY;
 69  
     }
 70  
 
 71  
     private Narrative createNarrative(String narrative) {
 72  3
         Pattern findElements = patternToPullNarrativeElementsIntoGroups();
 73  3
         Matcher findingElements = findElements.matcher(narrative);
 74  3
         if (findingElements.matches()) {
 75  2
             String inOrderTo = findingElements.group(1).trim();
 76  2
             String asA = findingElements.group(2).trim();
 77  2
             String iWantTo = findingElements.group(3).trim();
 78  2
             return new Narrative(inOrderTo, asA, iWantTo);
 79  
         }
 80  1
         return Narrative.EMPTY;
 81  
     }
 82  
 
 83  
     private List<Scenario> parseScenariosFrom(
 84  
             String storyAsText) {
 85  16
         List<Scenario> parsed = new ArrayList<Scenario>();
 86  16
         for (String scenarioAsText : splitScenarios(storyAsText)) {
 87  116
             parsed.add(parseScenario(scenarioAsText));
 88  
         }
 89  16
         return parsed;
 90  
     }
 91  
 
 92  
     private List<String> splitScenarios(String storyAsText) {
 93  16
         List<String> scenarios = new ArrayList<String>();
 94  16
         String scenarioKeyword = keywords.scenario();
 95  
 
 96  
         // remove anything after scenario keyword, if found
 97  16
         if ( StringUtils.contains(storyAsText, scenarioKeyword) ){
 98  8
             storyAsText = StringUtils.substringAfter(storyAsText, scenarioKeyword);
 99  
         }
 100  
 
 101  134
         for (String scenarioAsText : storyAsText.split(scenarioKeyword)) {
 102  118
             if (scenarioAsText.trim().length() > 0) {
 103  116
                 scenarios.add(scenarioKeyword + "\n" + scenarioAsText);
 104  
             }
 105  
         }
 106  16
         return scenarios;
 107  
     }
 108  
 
 109  
     private Scenario parseScenario(String scenarioAsText) {
 110  116
         String title = findScenarioTitle(scenarioAsText);
 111  116
         ExamplesTable examplesTable = findExamplesTable(scenarioAsText);
 112  116
         List<String> givenStoryPaths = findGivenStoryPaths(scenarioAsText);
 113  116
         List<String> steps = findSteps(scenarioAsText);
 114  116
         return new Scenario(title, givenStoryPaths, examplesTable, steps);
 115  
     }
 116  
 
 117  
     private String findScenarioTitle(String scenarioAsText) {
 118  116
         Matcher findingTitle = patternToPullScenarioTitleIntoGroupOne()
 119  
                 .matcher(scenarioAsText);
 120  116
         return findingTitle.find() ? findingTitle.group(1).trim() : NONE;
 121  
     }
 122  
 
 123  
     private ExamplesTable findExamplesTable(String scenarioAsText) {
 124  116
         Matcher findingTable = patternToPullExamplesTableIntoGroupOne()
 125  
                 .matcher(scenarioAsText);
 126  116
         String table = findingTable.find() ? findingTable.group(1).trim() : NONE;
 127  116
         return new ExamplesTable(table, keywords.examplesTableHeaderSeparator(), keywords.examplesTableValueSeparator());
 128  
     }
 129  
 
 130  
     private List<String> findGivenStoryPaths(String scenarioAsText) {
 131  116
         Matcher findingGivenStories = patternToPullGivenStoriesIntoGroupOne()
 132  
                 .matcher(scenarioAsText);
 133  116
         String givenStories = findingGivenStories.find() ? findingGivenStories.group(1).trim() : NONE;
 134  116
         List<String> givenStoryPaths = new ArrayList<String>();
 135  234
         for (String storyPath : givenStories.split(COMMA)) {
 136  118
             String trimmed = storyPath.trim();
 137  118
             if (trimmed.length() > 0) {
 138  5
                 givenStoryPaths.add(trimmed);
 139  
             }
 140  
         }
 141  116
         return givenStoryPaths;
 142  
     }
 143  
 
 144  
     private List<String> findSteps(String scenarioAsText) {
 145  116
         Matcher matcher = patternToPullStepsIntoGroupOne().matcher(scenarioAsText);
 146  116
         List<String> steps = new ArrayList<String>();
 147  116
         int startAt = 0;
 148  15157
         while (matcher.find(startAt)) {
 149  15041
             steps.add(StringUtils.substringAfter(matcher.group(1), "\n"));
 150  15041
             startAt = matcher.start(4);
 151  
         }
 152  116
         return steps;
 153  
     }
 154  
    
 155  
     // Regex Patterns
 156  
     
 157  
     private Pattern patternToPullDescriptionIntoGroupOne() {
 158  16
         String narrativeOrScenario = concatenateWithOr(keywords.narrative(), keywords.scenario());
 159  16
         return compile("(.*?)(" + narrativeOrScenario + ").*", DOTALL);
 160  
     }
 161  
 
 162  
     private Pattern patternToPullNarrativeIntoGroupOne() {
 163  16
         return compile(".*" + keywords.narrative() + "(.*?)\\s*(" + keywords.scenario() + ").*", DOTALL);
 164  
     }
 165  
 
 166  
     private Pattern patternToPullNarrativeElementsIntoGroups() {
 167  3
         return compile(".*" + keywords.inOrderTo() + "(.*)\\s*" + keywords.asA() + "(.*)\\s*" + keywords.iWantTo() + "(.*)", DOTALL);
 168  
     }
 169  
 
 170  
     private Pattern patternToPullScenarioTitleIntoGroupOne() {
 171  116
         String scenario = keywords.scenario();
 172  116
         String startingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 173  116
         return compile(scenario + "((.|\\n)*?)\\s*(" + startingWords + ").*");
 174  
     }
 175  
 
 176  
     private Pattern patternToPullGivenStoriesIntoGroupOne() {
 177  116
         String givenStories = keywords.givenStories();
 178  116
         String startingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 179  116
         return compile(".*" + givenStories + "((.|\\n)*?)\\s*(" + startingWords + ").*");
 180  
     }
 181  
 
 182  
     private Pattern patternToPullStepsIntoGroupOne() {
 183  116
         String initialStartingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 184  116
         String followingStartingWords = concatenateWithOr("\\n", "\\s", keywords.startingWords());
 185  116
         String examplesTable = keywords.examplesTable();
 186  116
         return compile("((" + initialStartingWords + ") (.)*?)\\s*(\\Z|"
 187  
                 + followingStartingWords + "|" + examplesTable + ")", DOTALL);
 188  
     }
 189  
 
 190  
     private Pattern patternToPullExamplesTableIntoGroupOne() {
 191  116
         String table = keywords.examplesTable();
 192  116
         return compile(".*" + table + "\\s*(.*)", DOTALL);
 193  
     }
 194  
 
 195  
     private String concatenateWithOr(String... keywords) {
 196  16
         return concatenateWithOr(null, null, keywords);
 197  
     }
 198  
 
 199  
     private String concatenateWithOr(String beforeKeyword, String afterKeyword, String[] keywords) {
 200  480
         StringBuilder builder = new StringBuilder();
 201  480
         String before = beforeKeyword != null ? beforeKeyword : NONE;
 202  480
         String after = afterKeyword != null ? afterKeyword : NONE;
 203  2832
         for (String keyword : keywords) {
 204  2352
             builder.append(before).append(keyword).append(after).append("|");
 205  
         }
 206  480
         return StringUtils.chomp(builder.toString(), "|"); // chop off the last "|"
 207  
     }
 208  
 
 209  
 }