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