Coverage Report - org.jbehave.core.parsers.RegexPrefixCapturingPatternParser
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexPrefixCapturingPatternParser
100%
42/42
100%
6/6
1.2
RegexPrefixCapturingPatternParser$Parameter
88%
8/9
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  529
 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  346
                 this(DEFAULT_PREFIX);
 46  346
         }
 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  347
                 this(prefix, DEFAULT_CHARACTER_CLASS);
 57  347
         }
 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  348
                         String characterClass) {
 70  348
                 this.prefix = prefix;
 71  348
                 this.characterClass = characterClass;
 72  348
         }
 73  
 
 74  
         public String getPrefix() {
 75  1
                 return prefix;
 76  
         }
 77  
 
 78  
         public StepMatcher parseStep(StepType stepType, String stepPattern) {
 79  359
                 String escapingPunctuation = escapingPunctuation(stepPattern);
 80  359
                 List<Parameter> parameters = findParameters(escapingPunctuation);
 81  359
                 Pattern regexPattern = buildPattern(escapingPunctuation, parameters);
 82  359
                 return new RegexStepMatcher(stepType, escapingPunctuation, regexPattern,
 83  359
                                 parameterNames(parameters));
 84  
         }
 85  
 
 86  
         private Pattern buildPattern(String stepPattern, List<Parameter> parameters) {
 87  718
                 return Pattern.compile(
 88  359
                                 parameterCapturingRegex(stepPattern, parameters),
 89  
                                 Pattern.DOTALL);
 90  
         }
 91  
 
 92  
         private String[] parameterNames(List<Parameter> parameters) {
 93  359
                 List<String> names = new ArrayList<String>();
 94  359
                 for (Parameter parameter : parameters) {
 95  529
                         names.add(parameter.name);
 96  529
                 }
 97  359
                 return names.toArray(new String[names.size()]);
 98  
         }
 99  
 
 100  
         private List<Parameter> findParameters(String pattern) {
 101  359
                 List<Parameter> parameters = new ArrayList<Parameter>();
 102  359
                 Matcher findingAllParameterNames = findingAllParameterNames().matcher(
 103  
                                 pattern);
 104  888
                 while (findingAllParameterNames.find()) {
 105  1058
                         parameters.add(new Parameter(pattern, findingAllParameterNames
 106  529
                                         .start(), findingAllParameterNames.end(),
 107  529
                                         findingAllParameterNames.group(2)));
 108  
                 }
 109  359
                 return parameters;
 110  
         }
 111  
 
 112  
         private Pattern findingAllParameterNames() {
 113  359
                 return Pattern.compile("(\\" + prefix + characterClass + "*)(\\W|\\Z)",
 114  
                                 Pattern.DOTALL);
 115  
         }
 116  
 
 117  
         private String escapingPunctuation(String pattern) {
 118  359
                 return pattern.replaceAll("([\\[\\]\\{\\}\\?\\^\\.\\*\\(\\)\\+\\\\])",
 119  
                                 "\\\\$1");
 120  
         }
 121  
 
 122  
         private String ignoringWhitespace(String pattern) {
 123  359
                 return pattern.replaceAll("\\s+", "\\\\s+");
 124  
         }
 125  
 
 126  
         private String parameterCapturingRegex(String stepPattern,
 127  
                         List<Parameter> parameters) {
 128  359
                 String regex = stepPattern;
 129  359
                 String capture = "(.*)";
 130  888
                 for (int i = parameters.size(); i > 0; i--) {
 131  529
                         Parameter parameter = parameters.get(i - 1);
 132  529
                         String start = regex.substring(0, parameter.start);
 133  529
                         String end = regex.substring(parameter.end);
 134  529
                         String whitespaceIfAny = parameter.whitespaceIfAny;
 135  529
                         regex = start + capture + whitespaceIfAny + end;
 136  
                 }
 137  359
                 return ignoringWhitespace(regex);
 138  
         }
 139  
 
 140  2116
         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  529
                                 String whitespaceIfAny) {
 148  529
                         this.start = start;
 149  529
                         this.end = end;
 150  529
                         this.whitespaceIfAny = whitespaceIfAny;
 151  1058
                         this.name = pattern.substring(start + prefix.length(),
 152  1058
                                         end - whitespaceIfAny.length()).trim();
 153  529
                 }
 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  
 }