Coverage Report - org.jbehave.core.parsers.RegexStoryParser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexStoryParser
98%
110/112
97%
43/44
1.871
 
 1  
 package org.jbehave.core.parsers;
 2  
 
 3  
 import java.io.File;
 4  
 import java.util.ArrayList;
 5  
 import java.util.List;
 6  
 import java.util.regex.Matcher;
 7  
 import java.util.regex.Pattern;
 8  
 
 9  
 import org.apache.commons.lang.StringUtils;
 10  
 import org.jbehave.core.configuration.Keywords;
 11  
 import org.jbehave.core.i18n.LocalizedKeywords;
 12  
 import org.jbehave.core.model.Description;
 13  
 import org.jbehave.core.model.ExamplesTable;
 14  
 import org.jbehave.core.model.ExamplesTableFactory;
 15  
 import org.jbehave.core.model.GivenStories;
 16  
 import org.jbehave.core.model.Meta;
 17  
 import org.jbehave.core.model.Narrative;
 18  
 import org.jbehave.core.model.Scenario;
 19  
 import org.jbehave.core.model.Story;
 20  
 
 21  
 import static java.util.regex.Pattern.DOTALL;
 22  
 import static java.util.regex.Pattern.compile;
 23  
 
 24  
 /**
 25  
  * Pattern-based story parser, which uses the keywords provided to parse the
 26  
  * textual story into a {@link Story}.
 27  
  */
 28  
 public class RegexStoryParser implements StoryParser {
 29  
 
 30  
     private static final String NONE = "";
 31  
     private final Keywords keywords;
 32  
     private final ExamplesTableFactory tableFactory;
 33  
 
 34  
     public RegexStoryParser() {
 35  31
         this(new LocalizedKeywords());
 36  31
     }
 37  
 
 38  
     public RegexStoryParser(Keywords keywords) {
 39  665
         this(keywords, new ExamplesTableFactory());
 40  665
     }
 41  
 
 42  
     public RegexStoryParser(ExamplesTableFactory tableFactory) {
 43  0
         this(new LocalizedKeywords(), tableFactory);
 44  0
     }
 45  
 
 46  665
     public RegexStoryParser(Keywords keywords, ExamplesTableFactory tableFactory) {
 47  665
         this.keywords = keywords;
 48  665
         this.tableFactory = tableFactory;
 49  665
     }
 50  
 
 51  
     public Story parseStory(String storyAsText) {
 52  1
         return parseStory(storyAsText, null);
 53  
     }
 54  
 
 55  
     public Story parseStory(String storyAsText, String storyPath) {
 56  21
         Description description = parseDescriptionFrom(storyAsText);
 57  21
         Meta meta = parseStoryMetaFrom(storyAsText);
 58  21
         Narrative narrative = parseNarrativeFrom(storyAsText);
 59  21
         List<Scenario> scenarios = parseScenariosFrom(storyAsText);
 60  21
         Story story = new Story(storyPath, description, meta, narrative, scenarios);
 61  21
         if (storyPath != null) {
 62  17
             story.namedAs(new File(storyPath).getName());
 63  
         }
 64  21
         return story;
 65  
     }
 66  
 
 67  
     private Description parseDescriptionFrom(String storyAsText) {
 68  21
         Matcher findingDescription = patternToPullDescriptionIntoGroupOne().matcher(storyAsText);
 69  21
         if (findingDescription.matches()) {
 70  12
             return new Description(findingDescription.group(1).trim());
 71  
         }
 72  9
         return Description.EMPTY;
 73  
     }
 74  
 
 75  
     private Meta parseStoryMetaFrom(String storyAsText) {
 76  21
         Matcher findingMeta = patternToPullStoryMetaIntoGroupOne().matcher(preScenarioText(storyAsText));
 77  21
         if (findingMeta.matches()) {
 78  3
             String meta = findingMeta.group(1).trim();
 79  3
             return createMeta(meta);
 80  
         }
 81  18
         return Meta.EMPTY;
 82  
     }
 83  
 
 84  
     private String preScenarioText(String storyAsText) {
 85  21
         String[] split = storyAsText.split(keywords.scenario());
 86  21
         return split.length > 0 ? split[0] : storyAsText;
 87  
     }
 88  
 
 89  
     private Meta createMeta(String meta) {
 90  7
         List<String> properties = new ArrayList<String>();
 91  24
         for (String property : meta.split(keywords.metaProperty())) {
 92  17
             if (StringUtils.isNotBlank(property)) {
 93  12
                 String beforeIgnorable = StringUtils.substringBefore(property,keywords.ignorable());
 94  12
                 if ( StringUtils.isNotBlank(beforeIgnorable)){
 95  10
                     properties.add(beforeIgnorable);
 96  
                 }
 97  
             }
 98  
         }
 99  7
         return new Meta(properties);
 100  
     }
 101  
 
 102  
     private Narrative parseNarrativeFrom(String storyAsText) {
 103  21
         Matcher findingNarrative = patternToPullNarrativeIntoGroupOne().matcher(storyAsText);
 104  21
         if (findingNarrative.matches()) {
 105  3
             String narrative = findingNarrative.group(1).trim();
 106  3
             return createNarrative(narrative);
 107  
         }
 108  18
         return Narrative.EMPTY;
 109  
     }
 110  
 
 111  
     private Narrative createNarrative(String narrative) {
 112  3
         Pattern findElements = patternToPullNarrativeElementsIntoGroups();
 113  3
         Matcher findingElements = findElements.matcher(narrative);
 114  3
         if (findingElements.matches()) {
 115  2
             String inOrderTo = findingElements.group(1).trim();
 116  2
             String asA = findingElements.group(2).trim();
 117  2
             String iWantTo = findingElements.group(3).trim();
 118  2
             return new Narrative(inOrderTo, asA, iWantTo);
 119  
         }
 120  1
         return Narrative.EMPTY;
 121  
     }
 122  
 
 123  
     private List<Scenario> parseScenariosFrom(String storyAsText) {
 124  21
         List<Scenario> parsed = new ArrayList<Scenario>();
 125  21
         for (String scenarioAsText : splitScenarios(storyAsText)) {
 126  122
             parsed.add(parseScenario(scenarioAsText));
 127  
         }
 128  21
         return parsed;
 129  
     }
 130  
 
 131  
     private List<String> splitScenarios(String storyAsText) {
 132  21
         List<String> scenarios = new ArrayList<String>();
 133  21
         String scenarioKeyword = keywords.scenario();
 134  
 
 135  
         // remove anything after scenario keyword, if found
 136  21
         if (StringUtils.contains(storyAsText, scenarioKeyword)) {
 137  12
             storyAsText = StringUtils.substringAfter(storyAsText, scenarioKeyword);
 138  
         }
 139  
 
 140  145
         for (String scenarioAsText : storyAsText.split(scenarioKeyword)) {
 141  124
             if (scenarioAsText.trim().length() > 0) {
 142  122
                 scenarios.add(scenarioKeyword + "\n" + scenarioAsText);
 143  
             }
 144  
         }
 145  21
         return scenarios;
 146  
     }
 147  
 
 148  
     private Scenario parseScenario(String scenarioAsText) {
 149  122
         String title = findScenarioTitle(scenarioAsText);
 150  122
         Meta meta = findScenarioMeta(scenarioAsText);
 151  122
         ExamplesTable examplesTable = findExamplesTable(scenarioAsText);
 152  122
         GivenStories givenStories = findGivenStories(scenarioAsText);
 153  122
         if (givenStories.requireParameters()) {
 154  1
             givenStories.useExamplesTable(examplesTable);
 155  
         }
 156  122
         List<String> steps = findSteps(scenarioAsText);
 157  122
         return new Scenario(title, meta, givenStories, examplesTable, steps);
 158  
     }
 159  
 
 160  
     private String findScenarioTitle(String scenarioAsText) {
 161  122
         Matcher findingTitle = patternToPullScenarioTitleIntoGroupOne().matcher(scenarioAsText);
 162  122
         return findingTitle.find() ? findingTitle.group(1).trim() : NONE;
 163  
     }
 164  
 
 165  
     private Meta findScenarioMeta(String scenarioAsText) {
 166  122
         Matcher findingMeta = patternToPullScenarioMetaIntoGroupOne().matcher(scenarioAsText);
 167  122
         if (findingMeta.matches()) {
 168  4
             String meta = findingMeta.group(1).trim();
 169  4
             return createMeta(meta);
 170  
         }
 171  118
         return Meta.EMPTY;
 172  
     }
 173  
 
 174  
     private ExamplesTable findExamplesTable(String scenarioAsText) {
 175  122
         Matcher findingTable = patternToPullExamplesTableIntoGroupOne().matcher(scenarioAsText);
 176  122
         String tableInput = findingTable.find() ? findingTable.group(1).trim() : NONE;
 177  122
         return tableFactory.createExamplesTable(tableInput);
 178  
     }
 179  
 
 180  
     private GivenStories findGivenStories(String scenarioAsText) {
 181  122
         Matcher findingGivenStories = patternToPullGivenStoriesIntoGroupOne().matcher(scenarioAsText);
 182  122
         String givenStories = findingGivenStories.find() ? findingGivenStories.group(1).trim() : NONE;
 183  122
         return new GivenStories(givenStories);
 184  
     }
 185  
 
 186  
     private List<String> findSteps(String scenarioAsText) {
 187  122
         Matcher matcher = patternToPullStepsIntoGroupOne().matcher(scenarioAsText);
 188  122
         List<String> steps = new ArrayList<String>();
 189  122
         int startAt = 0;
 190  15179
         while (matcher.find(startAt)) {
 191  15057
             steps.add(StringUtils.substringAfter(matcher.group(1), "\n"));
 192  15057
             startAt = matcher.start(4);
 193  
         }
 194  122
         return steps;
 195  
     }
 196  
 
 197  
     // Regex Patterns
 198  
 
 199  
     private Pattern patternToPullDescriptionIntoGroupOne() {
 200  21
         String metaOrNarrativeOrScenario = concatenateWithOr(keywords.meta(), keywords.narrative(), keywords.scenario());
 201  21
         return compile("(.*?)(" + metaOrNarrativeOrScenario + ").*", DOTALL);
 202  
     }
 203  
 
 204  
     private Pattern patternToPullStoryMetaIntoGroupOne() {
 205  21
         return compile(".*" + keywords.meta() + "(.*?)\\s*(\\Z|" + keywords.narrative() + ").*", DOTALL);
 206  
     }
 207  
 
 208  
     private Pattern patternToPullNarrativeIntoGroupOne() {
 209  21
         return compile(".*" + keywords.narrative() + "(.*?)\\s*(" + keywords.scenario() + ").*", DOTALL);
 210  
     }
 211  
 
 212  
     private Pattern patternToPullNarrativeElementsIntoGroups() {
 213  3
         return compile(".*" + keywords.inOrderTo() + "(.*)\\s*" + keywords.asA() + "(.*)\\s*" + keywords.iWantTo()
 214  
                 + "(.*)", DOTALL);
 215  
     }
 216  
 
 217  
     private Pattern patternToPullScenarioTitleIntoGroupOne() {
 218  122
         String startingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 219  122
         return compile(keywords.scenario() + "((.|\\n)*?)\\s*(" + keywords.meta() + "|" + startingWords + ").*", DOTALL);
 220  
     }
 221  
 
 222  
     private Pattern patternToPullScenarioMetaIntoGroupOne() {
 223  122
         String startingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 224  122
         return compile(".*" + keywords.meta() + "(.*?)\\s*(" + keywords.givenStories() + "|" + startingWords + ").*",
 225  
                 DOTALL);
 226  
     }
 227  
 
 228  
     private Pattern patternToPullGivenStoriesIntoGroupOne() {
 229  122
         String startingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 230  122
         return compile(".*\\n" + keywords.givenStories() + "((.|\\n)*?)\\s*(" + startingWords + ").*", DOTALL);
 231  
     }
 232  
 
 233  
     private Pattern patternToPullStepsIntoGroupOne() {
 234  122
         String initialStartingWords = concatenateWithOr("\\n", "", keywords.startingWords());
 235  122
         String followingStartingWords = concatenateWithOr("\\n", "\\s", keywords.startingWords());
 236  122
         return compile(
 237  
                 "((" + initialStartingWords + ") (.)*?)\\s*(\\Z|" + followingStartingWords + "|\\n"
 238  
                         + keywords.examplesTable() + ")", DOTALL);
 239  
     }
 240  
 
 241  
     private Pattern patternToPullExamplesTableIntoGroupOne() {
 242  122
         return compile(".*\\n" + keywords.examplesTable() + "\\s*(.*)", DOTALL);
 243  
     }
 244  
 
 245  
     private String concatenateWithOr(String... keywords) {
 246  21
         return concatenateWithOr(null, null, keywords);
 247  
     }
 248  
 
 249  
     private String concatenateWithOr(String beforeKeyword, String afterKeyword, String[] keywords) {
 250  631
         StringBuilder builder = new StringBuilder();
 251  631
         String before = beforeKeyword != null ? beforeKeyword : NONE;
 252  631
         String after = afterKeyword != null ? afterKeyword : NONE;
 253  3744
         for (String keyword : keywords) {
 254  3113
             builder.append(before).append(keyword).append(after).append("|");
 255  
         }
 256  631
         return StringUtils.chomp(builder.toString(), "|"); // chop off the last
 257  
                                                            // "|"
 258  
     }
 259  
 
 260  
 }