Coverage Report - org.jbehave.core.parsers.RegexPrefixCapturingPatternParser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexPrefixCapturingPatternParser
100%
38/38
100%
6/6
1.2
RegexPrefixCapturingPatternParser$Parameter
87%
7/8
N/A
1.2
 
 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  
  * The parameter names are by default assumed to be any unicode-supported
 18  
  * alphanumeric sequence, not limited to ASCII (see
 19  
  * http://www.regular-expressions.info/unicode.html), i.e. corresponding to
 20  
  * character class [\p{L}\p{N}\p{Pc}]. A different character class can optionally be
 21  
  * provided.
 22  
  * 
 23  
  * @author Elizabeth Keogh
 24  
  * @author Mauro Talevi
 25  
  */
 26  89
 public class RegexPrefixCapturingPatternParser implements StepPatternParser {
 27  
 
 28  
         /**
 29  
          * The default prefix to identify parameter names
 30  
          */
 31  
         private static final String DEFAULT_PREFIX = "$";
 32  
         /**
 33  
          * The default character class to match the parameter names.
 34  
          */
 35  
         private static final String DEFAULT_CHARACTER_CLASS = "[\\p{L}\\p{N}\\p{Pc}]";
 36  
 
 37  
         private final String prefix;
 38  
         private final String characterClass;
 39  
 
 40  
         /**
 41  
          * Creates a parser which captures parameters starting with $ in a matching
 42  
          * step and whose names are alphanumeric sequences.
 43  
          */
 44  
         public RegexPrefixCapturingPatternParser() {
 45  582
                 this(DEFAULT_PREFIX);
 46  582
         }
 47  
 
 48  
         /**
 49  
          * Creates a parser which captures parameters starting with a given prefix
 50  
          * in a matching step and whose names are alphanumeric sequences
 51  
          * 
 52  
          * @param prefix
 53  
          *            the prefix to use in capturing parameters
 54  
          */
 55  
         public RegexPrefixCapturingPatternParser(String prefix) {
 56  583
                 this(prefix, DEFAULT_CHARACTER_CLASS);
 57  583
         }
 58  
 
 59  
         /**
 60  
          * Creates a parser which captures parameters starting with a given prefix
 61  
          * in a matching step and a given character class.
 62  
          * 
 63  
          * @param prefix
 64  
          *            the prefix to use in capturing parameters
 65  
          * @param characterClass
 66  
          *            the regex character class to find parameter names
 67  
          */
 68  
         public RegexPrefixCapturingPatternParser(String prefix,
 69  584
                         String characterClass) {
 70  584
                 this.prefix = prefix;
 71  584
                 this.characterClass = characterClass;
 72  584
         }
 73  
 
 74  
         public String getPrefix() {
 75  1
                 return prefix;
 76  
         }
 77  
 
 78  
         public StepMatcher parseStep(StepType stepType, String stepPattern) {
 79  140
                 String escapingPunctuation = escapingPunctuation(stepPattern);
 80  140
                 List<Parameter> parameters = findParameters(escapingPunctuation);
 81  140
                 Pattern regexPattern = buildPattern(escapingPunctuation, parameters);
 82  140
                 return new RegexStepMatcher(stepType, escapingPunctuation, regexPattern,
 83  
                                 parameterNames(parameters));
 84  
         }
 85  
 
 86  
         private Pattern buildPattern(String stepPattern, List<Parameter> parameters) {
 87  140
                 return Pattern.compile(
 88  
                                 parameterCapturingRegex(stepPattern, parameters),
 89  
                                 Pattern.DOTALL);
 90  
         }
 91  
 
 92  
         private String[] parameterNames(List<Parameter> parameters) {
 93  140
                 List<String> names = new ArrayList<String>();
 94  140
                 for (Parameter parameter : parameters) {
 95  89
                         names.add(parameter.name);
 96  89
                 }
 97  140
                 return names.toArray(new String[names.size()]);
 98  
         }
 99  
 
 100  
         private List<Parameter> findParameters(String pattern) {
 101  140
                 List<Parameter> parameters = new ArrayList<Parameter>();
 102  140
                 Matcher findingAllParameterNames = findingAllParameterNames().matcher(
 103  
                                 pattern);
 104  229
                 while (findingAllParameterNames.find()) {
 105  89
                         parameters.add(new Parameter(pattern, findingAllParameterNames
 106  
                                         .start(), findingAllParameterNames.end(),
 107  
                                         findingAllParameterNames.group(2)));
 108  
                 }
 109  140
                 return parameters;
 110  
         }
 111  
 
 112  
         private Pattern findingAllParameterNames() {
 113  140
                 return Pattern.compile("(\\" + prefix + characterClass + "*)(\\W|\\Z)",
 114  
                                 Pattern.DOTALL);
 115  
         }
 116  
 
 117  
         private String escapingPunctuation(String pattern) {
 118  140
                 return pattern.replaceAll("([\\[\\]\\{\\}\\?\\^\\.\\*\\(\\)\\+\\\\])",
 119  
                                 "\\\\$1");
 120  
         }
 121  
 
 122  
         private String ignoringWhitespace(String pattern) {
 123  140
                 return pattern.replaceAll("\\s+", "\\\\s+");
 124  
         }
 125  
 
 126  
         private String parameterCapturingRegex(String stepPattern,
 127  
                         List<Parameter> parameters) {
 128  140
                 String regex = stepPattern;
 129  140
                 String capture = "(.*)";
 130  229
                 for (int i = parameters.size(); i > 0; i--) {
 131  89
                         Parameter parameter = parameters.get(i - 1);
 132  89
                         String start = regex.substring(0, parameter.start);
 133  89
                         String end = regex.substring(parameter.end);
 134  89
                         String whitespaceIfAny = parameter.whitespaceIfAny;
 135  89
                         regex = start + capture + whitespaceIfAny + end;
 136  
                 }
 137  140
                 return ignoringWhitespace(regex);
 138  
         }
 139  
 
 140  356
         private class Parameter {
 141  
                 private final int start;
 142  
                 private final int end;
 143  
                 private final String whitespaceIfAny;
 144  
                 private final String name;
 145  
 
 146  
                 public Parameter(String pattern, int start, int end,
 147  89
                                 String whitespaceIfAny) {
 148  89
                         this.start = start;
 149  89
                         this.end = end;
 150  89
                         this.whitespaceIfAny = whitespaceIfAny;
 151  89
                         this.name = pattern.substring(start + prefix.length(),
 152  
                                         end - whitespaceIfAny.length()).trim();
 153  89
                 }
 154  
 
 155  
                 @Override
 156  
                 public String toString() {
 157  0
                         return name;
 158  
                 }
 159  
         }
 160  
 
 161  
         @Override
 162  
         public String toString() {
 163  1
                 return ToStringBuilder.reflectionToString(this,
 164  
                                 ToStringStyle.SHORT_PREFIX_STYLE);
 165  
         }
 166  
 }