Coverage Report - org.jbehave.core.parsers.RegexPrefixCapturingPatternParser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexPrefixCapturingPatternParser
100%
33/33
100%
6/6
1.231
RegexPrefixCapturingPatternParser$Parameter
100%
7/7
N/A
1.231
 
 1  
 package org.jbehave.core.parsers;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.List;
 5  
 import java.util.regex.Matcher;
 6  
 import java.util.regex.Pattern;
 7  
 
 8  
 import org.apache.commons.lang.builder.ToStringBuilder;
 9  
 import org.apache.commons.lang.builder.ToStringStyle;
 10  
 import org.jbehave.core.steps.StepType;
 11  
 
 12  
 /**
 13  
  * A step pattern parser that provides a step matcher which will capture
 14  
  * parameters starting with the given prefix in any matching step. Default
 15  
  * prefix is $.
 16  
  * 
 17  
  * @author Elizabeth Keogh
 18  
  */
 19  162
 public class RegexPrefixCapturingPatternParser implements StepPatternParser {
 20  
 
 21  
     private final String prefix;
 22  
 
 23  
         /**
 24  
          * Creates a parser which captures parameters starting with $ in a matching
 25  
          * step.
 26  
          */
 27  
         public RegexPrefixCapturingPatternParser() {
 28  602
                 this("$");
 29  602
         }
 30  
 
 31  
         /**
 32  
          * Creates a parser which captures parameters starting with a given prefix
 33  
          * in a matching step.
 34  
          * 
 35  
          * @param prefix the prefix to use in capturing parameters
 36  
          */
 37  603
         public RegexPrefixCapturingPatternParser(String prefix) {
 38  603
                 this.prefix = prefix;
 39  603
         }
 40  
 
 41  
         public String getPrefix(){
 42  1
             return prefix;
 43  
         }
 44  
         
 45  
         public StepMatcher parseStep(StepType stepType, String stepPattern) {
 46  131
                 return new RegexStepMatcher(stepType, stepPattern,
 47  
                                 buildPattern(stepPattern), extractParameterNames(stepPattern));
 48  
         }
 49  
 
 50  
         private Pattern buildPattern(String stepPattern) {
 51  131
                 String matchThisButLeaveBrackets = escapeRegexPunctuation(stepPattern);
 52  131
                 String patternToMatchAgainst = replaceParametersWithCapture(
 53  
                                 matchThisButLeaveBrackets, findParametersToReplace(matchThisButLeaveBrackets));
 54  131
                 String matchThisButIgnoreWhitespace = anyWhitespaceWillDo(patternToMatchAgainst);
 55  131
                 return Pattern.compile(matchThisButIgnoreWhitespace, Pattern.DOTALL);
 56  
         }
 57  
 
 58  
     private String escapeRegexPunctuation(String matchThis) {
 59  131
         return matchThis.replaceAll("([\\[\\]\\{\\}\\?\\^\\.\\*\\(\\)\\+\\\\])", "\\\\$1");
 60  
     }
 61  
 
 62  
         private String anyWhitespaceWillDo(String matchThis) {
 63  131
                 return matchThis.replaceAll("\\s+", "\\\\s+");
 64  
         }
 65  
 
 66  
         private String[] extractParameterNames(String stepPattern) {
 67  131
                 List<String> names = new ArrayList<String>();
 68  131
                 for (Parameter parameter : findParametersToReplace(stepPattern)) {
 69  81
                         names.add(parameter.name);
 70  81
                 }
 71  131
                 return names.toArray(new String[names.size()]);
 72  
         }
 73  
 
 74  
     private List<Parameter> findParametersToReplace(String matchThisButLeaveBrackets) {
 75  262
         List<Parameter> parameters = new ArrayList<Parameter>();
 76  262
         Matcher findingAllPrefixedWords = findAllPrefixedWords().matcher(matchThisButLeaveBrackets);
 77  424
         while (findingAllPrefixedWords.find()) {
 78  162
             parameters.add(new Parameter(matchThisButLeaveBrackets, findingAllPrefixedWords.start(),
 79  
                     findingAllPrefixedWords.end(), findingAllPrefixedWords.group(2)));
 80  
         }
 81  262
         return parameters;
 82  
     }
 83  
 
 84  
     private Pattern findAllPrefixedWords() {
 85  
         // Use \p{L} in place of \w to allow for all unicode-supported letters, not only ASCII
 86  262
         return Pattern.compile("(\\" + prefix + "\\p{L}*)(\\W|\\Z)", Pattern.DOTALL);
 87  
     }
 88  
 
 89  
         private String replaceParametersWithCapture(String escapedMatch,
 90  
                         List<Parameter> parameters) {
 91  131
         String replaced = escapedMatch;
 92  212
         for (int i = parameters.size(); i > 0; i--) {
 93  81
             String start = replaced.substring(0, parameters.get(i - 1).start);
 94  81
             String end = replaced.substring(parameters.get(i - 1).end);
 95  81
             String whitespaceIfAny = parameters.get(i - 1).whitespaceIfAny;
 96  81
             replaced = start + "(.*)" + whitespaceIfAny + end;
 97  
         }
 98  131
         return replaced;
 99  
         }
 100  
 
 101  324
         private class Parameter {
 102  
                 private final int start;
 103  
                 private final int end;
 104  
                 private final String whitespaceIfAny;
 105  
                 private final String name;
 106  
 
 107  
                 public Parameter(String pattern, int start, int end,
 108  162
                                 String whitespaceIfAny) {
 109  162
                         this.start = start;
 110  162
                         this.end = end;
 111  162
                         this.whitespaceIfAny = whitespaceIfAny;
 112  162
                         this.name = pattern.substring(start + prefix.length(), end - whitespaceIfAny.length()).trim();
 113  162
                 }
 114  
 
 115  
         }
 116  
 
 117  
     @Override
 118  
     public String toString() {
 119  1
         return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
 120  
     }
 121  
 }