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