Coverage Report - org.jbehave.core.steps.StepCreator
 
Classes in this File Line Coverage Branch Coverage Complexity
StepCreator
95%
152/159
87%
78/89
1.976
StepCreator$1
100%
1/1
N/A
1.976
StepCreator$AbstractStep
33%
1/3
N/A
1.976
StepCreator$BeforeOrAfterStep
81%
13/16
N/A
1.976
StepCreator$BeforeOrAfterStep$UUIDExceptionWrapperInjector
100%
5/5
100%
2/2
1.976
StepCreator$IgnorableStep
83%
5/6
N/A
1.976
StepCreator$Jsr330Helper
100%
2/2
N/A
1.976
StepCreator$MethodInvoker
100%
29/29
100%
10/10
1.976
StepCreator$MethodInvoker$Parameter
100%
9/9
100%
2/2
1.976
StepCreator$ParameterName
100%
5/5
N/A
1.976
StepCreator$ParameterNotFound
100%
4/4
N/A
1.976
StepCreator$ParameterisedStep
88%
38/43
75%
9/12
1.976
StepCreator$PendingStep
66%
8/12
0%
0/2
1.976
StepCreator$UponFailureStep
83%
5/6
N/A
1.976
StepCreator$UponSuccessStep
83%
5/6
N/A
1.976
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import java.lang.annotation.Annotation;
 4  
 import java.lang.reflect.InvocationTargetException;
 5  
 import java.lang.reflect.Method;
 6  
 import java.lang.reflect.Type;
 7  
 import java.util.HashMap;
 8  
 import java.util.Map;
 9  
 import java.util.regex.Matcher;
 10  
 import java.util.regex.Pattern;
 11  
 
 12  
 import org.apache.commons.lang.builder.ToStringBuilder;
 13  
 import org.apache.commons.lang.builder.ToStringStyle;
 14  
 import org.jbehave.core.annotations.AfterScenario.Outcome;
 15  
 import org.jbehave.core.annotations.Named;
 16  
 import org.jbehave.core.configuration.Keywords;
 17  
 import org.jbehave.core.failures.BeforeOrAfterFailed;
 18  
 import org.jbehave.core.failures.RestartingScenarioFailure;
 19  
 import org.jbehave.core.failures.UUIDExceptionWrapper;
 20  
 import org.jbehave.core.model.ExamplesTable;
 21  
 import org.jbehave.core.model.Meta;
 22  
 import org.jbehave.core.parsers.StepMatcher;
 23  
 import org.jbehave.core.reporters.StoryReporter;
 24  
 
 25  
 import com.thoughtworks.paranamer.NullParanamer;
 26  
 import com.thoughtworks.paranamer.Paranamer;
 27  
 
 28  
 import static java.util.Arrays.asList;
 29  
 import static org.jbehave.core.steps.AbstractStepResult.failed;
 30  
 import static org.jbehave.core.steps.AbstractStepResult.ignorable;
 31  
 import static org.jbehave.core.steps.AbstractStepResult.notPerformed;
 32  
 import static org.jbehave.core.steps.AbstractStepResult.pending;
 33  
 import static org.jbehave.core.steps.AbstractStepResult.skipped;
 34  
 import static org.jbehave.core.steps.AbstractStepResult.successful;
 35  
 
 36  797
 public class StepCreator {
 37  
 
 38  
         public static final String PARAMETER_TABLE_START = "\uff3b";
 39  
     public static final String PARAMETER_TABLE_END = "\uff3d";
 40  
     public static final String PARAMETER_VALUE_START = "\uFF5F";
 41  
     public static final String PARAMETER_VALUE_END = "\uFF60";
 42  
     public static final String PARAMETER_VALUE_NEWLINE = "\u2424";
 43  1
     public static final UUIDExceptionWrapper NO_FAILURE = new UUIDExceptionWrapper("no failure");
 44  
         private static final String NEWLINE = "\n";
 45  
     private static final String SPACE = " ";
 46  
         private static final String NONE = "";
 47  
     private final Class<?> stepsType;
 48  
     private final InjectableStepsFactory stepsFactory;
 49  
     private final ParameterConverters parameterConverters;
 50  
     private final ParameterControls parameterControls;
 51  
     private final Pattern delimitedNamePattern;
 52  
     private final StepMatcher stepMatcher;
 53  
     private StepMonitor stepMonitor;
 54  189
     private Paranamer paranamer = new NullParanamer();
 55  189
     private boolean dryRun = false;
 56  
 
 57  
     public StepCreator(Class<?> stepsType, InjectableStepsFactory stepsFactory,
 58  
             ParameterConverters parameterConverters, ParameterControls parameterControls, StepMatcher stepMatcher,
 59  189
             StepMonitor stepMonitor) {
 60  189
         this.stepsType = stepsType;
 61  189
         this.stepsFactory = stepsFactory;
 62  189
         this.parameterConverters = parameterConverters;
 63  189
         this.parameterControls = parameterControls;
 64  189
         this.stepMatcher = stepMatcher;
 65  189
         this.stepMonitor = stepMonitor;
 66  189
         this.delimitedNamePattern = Pattern.compile(parameterControls.nameDelimiterLeft() + "(\\w+?)"
 67  
                 + parameterControls.nameDelimiterRight());
 68  189
     }
 69  
 
 70  
     public void useStepMonitor(StepMonitor stepMonitor) {
 71  84
         this.stepMonitor = stepMonitor;
 72  84
     }
 73  
 
 74  
     public void useParanamer(Paranamer paranamer) {
 75  88
         this.paranamer = paranamer;
 76  88
     }
 77  
 
 78  
     public void doDryRun(boolean dryRun) {
 79  83
         this.dryRun = dryRun;
 80  83
     }
 81  
 
 82  
     public Object stepsInstance() {
 83  119
         return stepsFactory.createInstanceOfType(stepsType);
 84  
     }
 85  
 
 86  
     public Step createBeforeOrAfterStep(Method method, Meta meta) {
 87  30
         return new BeforeOrAfterStep(method, meta);
 88  
     }
 89  
 
 90  
     public Step createAfterStepUponOutcome(final Method method, final Outcome outcome, Meta storyAndScenarioMeta) {
 91  18
         switch (outcome) {
 92  
         case ANY:
 93  
         default:
 94  8
             return new BeforeOrAfterStep(method, storyAndScenarioMeta);
 95  
         case SUCCESS:
 96  5
             return new UponSuccessStep(method, storyAndScenarioMeta);
 97  
         case FAILURE:
 98  5
             return new UponFailureStep(method, storyAndScenarioMeta);
 99  
         }
 100  
     }
 101  
 
 102  
     public Map<String, String> matchedParameters(final Method method, final String stepAsString,
 103  
             final String stepWithoutStartingWord, final Map<String, String> namedParameters) {
 104  8
         Map<String, String> matchedParameters = new HashMap<String, String>(); 
 105  8
         if (stepMatcher.find(stepWithoutStartingWord)) { 
 106  
             // we've found a match, populate map
 107  4
             ParameterName[] parameterNames = parameterNames(method);
 108  4
             Type[] types = method.getGenericParameterTypes();
 109  4
             String[] values = parameterValuesForStep(namedParameters, types, parameterNames);
 110  
     
 111  
             
 112  7
             for (int i = 0; i < parameterNames.length; i++) {
 113  3
                 String name = parameterNames[i].name;
 114  3
                 if (name == null) {
 115  0
                     name = stepMatcher.parameterNames()[i];
 116  
                 }
 117  3
                 matchedParameters.put(name, values[i]);
 118  
             }
 119  
         }
 120  
         // else return empty map
 121  8
         return matchedParameters; 
 122  
     }
 123  
 
 124  
     /**
 125  
      * Returns the {@link ParameterName} representations for the method,
 126  
      * providing an abstraction that supports both annotated and non-annotated
 127  
      * parameters.
 128  
      * 
 129  
      * @param method the Method
 130  
      * @return The array of {@link ParameterName}s
 131  
      */
 132  
     private ParameterName[] parameterNames(Method method) {
 133  90
         String[] annotatedNames = annotatedParameterNames(method);
 134  90
         String[] paranamerNames = paranamerParameterNames(method);
 135  
 
 136  90
         ParameterName[] parameterNames = new ParameterName[annotatedNames.length];
 137  166
         for (int i = 0; i < annotatedNames.length; i++) {
 138  76
             parameterNames[i] = parameterName(annotatedNames, paranamerNames, i);
 139  
         }
 140  90
         return parameterNames;
 141  
     }
 142  
 
 143  
     private ParameterName parameterName(String[] annotatedNames, String[] paranamerNames, int i) {
 144  76
         String name = annotatedNames[i];
 145  76
         boolean annotated = true;
 146  76
         if (name == null) {
 147  33
             name = (paranamerNames.length > i ? paranamerNames[i] : null);
 148  33
             annotated = false;
 149  
         }
 150  76
         return new ParameterName(name, annotated);
 151  
     }
 152  
 
 153  
     /**
 154  
      * Extract parameter names using {@link Named}-annotated parameters
 155  
      * 
 156  
      * @param method the Method with {@link Named}-annotated parameters
 157  
      * @return An array of annotated parameter names, which <b>may</b> include
 158  
      *         <code>null</code> values for parameters that are not annotated
 159  
      */
 160  
     private String[] annotatedParameterNames(Method method) {
 161  129
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
 162  129
         String[] names = new String[parameterAnnotations.length];
 163  229
         for (int i = 0; i < parameterAnnotations.length; i++) {
 164  159
             for (Annotation annotation : parameterAnnotations[i]) {
 165  59
                 names[i] = annotationName(annotation);
 166  
             }
 167  
         }
 168  129
         return names;
 169  
     }
 170  
 
 171  
     /**
 172  
      * Returns either the value of the annotation, either {@link Named} or
 173  
      * "javax.inject.Named".
 174  
      * 
 175  
      * @param annotation the Annotation
 176  
      * @return The annotated value or <code>null</code> if no annotation is
 177  
      *         found
 178  
      */
 179  
     private String annotationName(Annotation annotation) {
 180  59
         if (annotation.annotationType().isAssignableFrom(Named.class)) {
 181  53
             return ((Named) annotation).value();
 182  6
         } else if ("javax.inject.Named".equals(annotation.annotationType().getName())) {
 183  6
             return Jsr330Helper.getNamedValue(annotation);
 184  
         } else {
 185  0
             return null;
 186  
         }
 187  
     }
 188  
 
 189  
     /**
 190  
      * Extract parameter names using
 191  
      * {@link Paranamer#lookupParameterNames(AccessibleObject, boolean)}
 192  
      * 
 193  
      * @param method the Method inspected by Paranamer
 194  
      * @return An array of parameter names looked up by Paranamer
 195  
      */
 196  
     private String[] paranamerParameterNames(Method method) {
 197  90
         return paranamer.lookupParameterNames(method, false);
 198  
     }
 199  
 
 200  
     public Step createParametrisedStep(final Method method, final String stepAsString,
 201  
             final String stepWithoutStartingWord, final Map<String, String> namedParameters) {
 202  89
         return new ParameterisedStep(stepAsString, method, stepWithoutStartingWord, namedParameters);
 203  
     }
 204  
 
 205  
     private String parametrisedStep(String stepAsString, Map<String, String> namedParameters, Type[] types,
 206  
             ParameterName[] names, String[] parameterValues) {
 207  84
             String parametrisedStep = stepAsString;
 208  
             // mark parameter values that are parsed
 209  153
         for (int position = 0; position < types.length; position++) {
 210  69
             parametrisedStep = markParsedParameterValue(parametrisedStep, types[position], parameterValues[position]);
 211  
         }
 212  
         // mark parameter values that are named
 213  84
         for (String name : namedParameters.keySet()) {
 214  30
             parametrisedStep = markNamedParameterValue(parametrisedStep, namedParameters, name);
 215  30
         }
 216  
 
 217  84
         return parametrisedStep;
 218  
     }
 219  
 
 220  
     private String markNamedParameterValue(String stepText, Map<String, String> namedParameters, String name) {
 221  30
         String value = namedParameter(namedParameters, name);
 222  30
         if (value != null) {
 223  30
                         stepText = stepText.replace(delimitedName(name), markedValue(value));
 224  
         }
 225  30
         return stepText;
 226  
     }
 227  
 
 228  
         private String delimitedName(String name) {
 229  30
                 return parameterControls.nameDelimiterLeft() + name + parameterControls.nameDelimiterRight();
 230  
         }
 231  
 
 232  
     private String markParsedParameterValue(String stepText, Type type, String value) {
 233  69
         if (value != null) {
 234  69
             if (isTable(type)) {
 235  0
                 stepText = stepText.replace(value, markedTable(value));
 236  
             } else {
 237  
                 // only mark non-empty string as parameter (JBEHAVE-656)                    
 238  69
                 if (value.trim().length() != 0) {
 239  68
                         String markedValue = markedValue(value);
 240  
                         // identify parameter values to mark as padded by spaces to avoid duplicated replacements of overlapping values (JBEHAVE-837)
 241  68
                         String leftPad = SPACE;
 242  68
                         String rightPad = ( stepText.endsWith(value) ? NONE : SPACE );
 243  68
                             stepText = stepText.replace(pad(value, leftPad, rightPad), pad(markedValue, leftPad, rightPad));
 244  
                 }
 245  69
                 stepText = stepText.replace(NEWLINE, PARAMETER_VALUE_NEWLINE);
 246  
             }
 247  
         }
 248  69
         return stepText;
 249  
     }
 250  
 
 251  
         private String markedTable(String value) {
 252  0
                 return pad(value, PARAMETER_TABLE_START, PARAMETER_TABLE_END);
 253  
         }
 254  
 
 255  
         private String markedValue(String value) {
 256  98
                 return pad(value, PARAMETER_VALUE_START, PARAMETER_VALUE_END);
 257  
         }
 258  
 
 259  
         private String pad(String value, String left, String right){
 260  234
                 return new StringBuilder().append(left).append(value).append(right).toString();
 261  
         }
 262  
         
 263  
     private boolean isTable(Type type) {
 264  69
         return type instanceof Class && ((Class<?>) type).isAssignableFrom(ExamplesTable.class);
 265  
     }
 266  
 
 267  
     private String[] parameterValuesForStep(Map<String, String> namedParameters, Type[] types, ParameterName[] names) {
 268  90
         final String[] parameters = new String[types.length];
 269  162
         for (int position = 0; position < types.length; position++) {
 270  74
             parameters[position] = parameterForPosition(position, names, namedParameters);
 271  
         }
 272  88
         return parameters;
 273  
     }
 274  
 
 275  
     private Object[] convertParameterValues(String[] valuesAsString, Type[] types) {
 276  84
         final Object[] parameters = new Object[valuesAsString.length];
 277  153
         for (int position = 0; position < valuesAsString.length; position++) {
 278  69
             parameters[position] = parameterConverters.convert(valuesAsString[position], types[position]);
 279  
         }
 280  84
         return parameters;
 281  
     }
 282  
 
 283  
     private String parameterForPosition(int position, ParameterName[] names, Map<String, String> namedParameters) {
 284  74
         int namePosition = parameterPosition(names, position);
 285  74
         String parameter = null;
 286  
 
 287  74
         if (namePosition != -1) {
 288  47
             String name = names[position].name;
 289  47
             boolean annotated = names[position].annotated;
 290  
 
 291  47
             boolean delimitedNamedParameters = false;
 292  
 
 293  47
             if (isGroupName(name)) {
 294  31
                 parameter = matchedParameter(name);
 295  31
                 String delimitedName = delimitedNameFor(parameter);
 296  
 
 297  31
                 if (delimitedName != null) {
 298  0
                     name = delimitedName;
 299  0
                     delimitedNamedParameters = true;
 300  
                 } else {
 301  31
                     monitorUsingNameForParameter(name, position, annotated);
 302  
                 }
 303  
             }
 304  
 
 305  47
             if (delimitedNamedParameters || isTableName(namedParameters, name)) {
 306  21
                 monitorUsingTableNameForParameter(name, position, annotated);
 307  21
                 parameter = namedParameter(namedParameters, name);
 308  
             }
 309  
 
 310  
         }
 311  
 
 312  74
         if (parameter == null) {
 313  31
             stepMonitor.usingNaturalOrderForParameter(position);
 314  31
             parameter = matchedParameter(position);
 315  29
             String delimitedName = delimitedNameFor(parameter);
 316  
 
 317  29
             if (delimitedName != null && isTableName(namedParameters, delimitedName)) {
 318  3
                 parameter = namedParameter(namedParameters, delimitedName);
 319  
             }
 320  
         }
 321  
 
 322  72
         stepMonitor.foundParameter(parameter, position);
 323  
 
 324  72
         return parameter;
 325  
     }
 326  
 
 327  
     private void monitorUsingTableNameForParameter(String name, int position, boolean usingAnnotationNames) {
 328  21
         if (usingAnnotationNames) {
 329  19
             stepMonitor.usingTableAnnotatedNameForParameter(name, position);
 330  
         } else {
 331  2
             stepMonitor.usingTableParameterNameForParameter(name, position);
 332  
         }
 333  21
     }
 334  
 
 335  
     private void monitorUsingNameForParameter(String name, int position, boolean usingAnnotationNames) {
 336  31
         if (usingAnnotationNames) {
 337  27
             stepMonitor.usingAnnotatedNameForParameter(name, position);
 338  
         } else {
 339  4
             stepMonitor.usingParameterNameForParameter(name, position);
 340  
         }
 341  31
     }
 342  
 
 343  
     private String delimitedNameFor(String parameter) {
 344  60
         if ( !parameterControls.delimiterNamedParameters() ){
 345  57
             return null;
 346  
         }
 347  3
         Matcher matcher = delimitedNamePattern.matcher(parameter);
 348  3
         return matcher.matches() ? matcher.group(1) : null;
 349  
     }
 350  
 
 351  
     String matchedParameter(String name) {
 352  32
         String[] parameterNames = stepMatcher.parameterNames();
 353  46
         for (int i = 0; i < parameterNames.length; i++) {
 354  45
             String parameterName = parameterNames[i];
 355  45
             if (name.equals(parameterName)) {
 356  31
                 return matchedParameter(i);
 357  
             }
 358  
         }
 359  1
         throw new ParameterNotFound(name, parameterNames);
 360  
     }
 361  
 
 362  
     private String matchedParameter(int position) {
 363  62
         String[] parameterNames = stepMatcher.parameterNames();
 364  62
         int matchedPosition = position + 1;
 365  62
         if (matchedPosition <= parameterNames.length) {
 366  60
             return stepMatcher.parameter(matchedPosition);
 367  
         }
 368  2
         throw new ParameterNotFound(position, parameterNames);
 369  
     }
 370  
 
 371  
     private int parameterPosition(ParameterName[] names, int position) {
 372  74
         if (names.length == 0) {
 373  0
             return -1;
 374  
         }
 375  74
         String positionName = names[position].name;
 376  122
         for (int i = 0; i < names.length; i++) {
 377  95
             String name = names[i].name;
 378  95
             if (name != null && positionName.equals(name)) {
 379  47
                 return i;
 380  
             }
 381  
         }
 382  27
         return -1;
 383  
     }
 384  
 
 385  
     private boolean isGroupName(String name) {
 386  47
         String[] groupNames = stepMatcher.parameterNames();
 387  65
         for (String groupName : groupNames) {
 388  49
             if (name.equals(groupName)) {
 389  31
                 return true;
 390  
             }
 391  
         }
 392  16
         return false;
 393  
     }
 394  
 
 395  
     private String namedParameter(Map<String, String> namedParameters, String name) {
 396  104
         return namedParameters.get(name);
 397  
     }
 398  
 
 399  
     private boolean isTableName(Map<String, String> namedParameters, String name) {
 400  50
         return namedParameter(namedParameters, name) != null;
 401  
     }
 402  
 
 403  
     public static Step createPendingStep(final String stepAsString, String previousNonAndStep) {
 404  28
         return new PendingStep(stepAsString, previousNonAndStep);
 405  
     }
 406  
 
 407  
     public static Step createIgnorableStep(final String stepAsString) {
 408  2
         return new IgnorableStep(stepAsString);
 409  
     }
 410  
 
 411  
     /**
 412  
      * This is a different class, because the @Inject jar may not be in the
 413  
      * classpath.
 414  
      */
 415  6
     public static class Jsr330Helper {
 416  
 
 417  
         private static String getNamedValue(Annotation annotation) {
 418  6
             return ((javax.inject.Named) annotation).value();
 419  
         }
 420  
 
 421  
     }
 422  
 
 423  
     @SuppressWarnings("serial")
 424  
     public static class ParameterNotFound extends RuntimeException {
 425  
 
 426  
         public ParameterNotFound(String name, String[] parameters) {
 427  1
             super("Parameter not found for name '" + name + "' amongst '" + asList(parameters) + "'");
 428  1
         }
 429  
 
 430  
         public ParameterNotFound(int position, String[] parameters) {
 431  2
             super("Parameter not found for position '" + position + "' amongst '" + asList(parameters) + "'");
 432  2
         }
 433  
     }
 434  
 
 435  178
     public static abstract class AbstractStep implements Step {
 436  
 
 437  
             public String asString(Keywords keywords) {
 438  0
                         return toString();
 439  
                 }
 440  
             
 441  
         @Override
 442  
         public String toString() {
 443  0
             return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
 444  
         }
 445  
 
 446  
     }
 447  
 
 448  
     private class BeforeOrAfterStep extends AbstractStep {
 449  
         private final Method method;
 450  
         private final Meta meta;
 451  
 
 452  48
         public BeforeOrAfterStep(Method method, Meta meta) {
 453  48
             this.method = method;
 454  48
             this.meta = meta;
 455  48
         }
 456  
 
 457  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 458  39
             ParameterConverters paramConvertersWithExceptionInjector = paramConvertersWithExceptionInjector(storyFailureIfItHappened);
 459  39
             MethodInvoker methodInvoker = new MethodInvoker(method, paramConvertersWithExceptionInjector, paranamer,
 460  
                     meta);
 461  
 
 462  
             try {
 463  39
                 methodInvoker.invoke();
 464  4
             } catch (InvocationTargetException e) {
 465  4
                 return failed(method, new UUIDExceptionWrapper(new BeforeOrAfterFailed(method, e.getCause())));
 466  0
             } catch (Throwable t) {
 467  0
                 return failed(method, new UUIDExceptionWrapper(new BeforeOrAfterFailed(method, t)));
 468  35
             }
 469  
 
 470  35
             return skipped();
 471  
         }
 472  
 
 473  
         private ParameterConverters paramConvertersWithExceptionInjector(UUIDExceptionWrapper storyFailureIfItHappened) {
 474  39
             return parameterConverters.newInstanceAdding(new UUIDExceptionWrapperInjector(storyFailureIfItHappened));
 475  
         }
 476  
 
 477  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 478  6
             return perform(storyFailureIfItHappened);
 479  
         }
 480  
 
 481  
         private class UUIDExceptionWrapperInjector implements ParameterConverters.ParameterConverter {
 482  
             private final UUIDExceptionWrapper storyFailureIfItHappened;
 483  
 
 484  39
             public UUIDExceptionWrapperInjector(UUIDExceptionWrapper storyFailureIfItHappened) {
 485  39
                 this.storyFailureIfItHappened = storyFailureIfItHappened;
 486  39
             }
 487  
 
 488  
             public boolean accept(Type type) {
 489  14
                 return UUIDExceptionWrapper.class == type;
 490  
             }
 491  
 
 492  
             public Object convertValue(String value, Type type) {
 493  6
                 return storyFailureIfItHappened;
 494  
             }
 495  
         }
 496  
 
 497  
                 public String asString(Keywords keywords) {
 498  0
                         return method.getName()+";"+meta.asString(keywords);
 499  
                 }
 500  
     }
 501  
 
 502  
     public class UponSuccessStep extends AbstractStep {
 503  
         private BeforeOrAfterStep beforeOrAfterStep;
 504  
 
 505  5
         public UponSuccessStep(Method method, Meta storyAndScenarioMeta) {
 506  5
             this.beforeOrAfterStep = new BeforeOrAfterStep(method, storyAndScenarioMeta);
 507  5
         }
 508  
 
 509  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 510  3
             return skipped();
 511  
         }
 512  
 
 513  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 514  2
             return beforeOrAfterStep.perform(storyFailureIfItHappened);
 515  
         }
 516  
 
 517  
                 public String asString(Keywords keywords) {
 518  0
                         return beforeOrAfterStep.asString(keywords);
 519  
                 }
 520  
 
 521  
     }
 522  
 
 523  
     public class UponFailureStep extends AbstractStep {
 524  
         private final BeforeOrAfterStep beforeOrAfterStep;
 525  
 
 526  5
         public UponFailureStep(Method method, Meta storyAndScenarioMeta) {
 527  5
             this.beforeOrAfterStep = new BeforeOrAfterStep(method, storyAndScenarioMeta);
 528  5
         }
 529  
 
 530  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 531  3
             return beforeOrAfterStep.perform(storyFailureIfItHappened);
 532  
         }
 533  
 
 534  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 535  2
             return skipped();
 536  
         }
 537  
 
 538  
                 public String asString(Keywords keywords) {
 539  0
                         return beforeOrAfterStep.asString(keywords);
 540  
                 }
 541  
     
 542  
     }
 543  
 
 544  
     public class ParameterisedStep extends AbstractStep {
 545  
         private Object[] convertedParameters;
 546  
         private String parametrisedStep;
 547  
         private final String stepAsString;
 548  
         private final Method method;
 549  
         private final String stepWithoutStartingWord;
 550  
         private final Map<String, String> namedParameters;
 551  
 
 552  
         public ParameterisedStep(String stepAsString, Method method, String stepWithoutStartingWord,
 553  89
                 Map<String, String> namedParameters) {
 554  89
             this.stepAsString = stepAsString;
 555  89
             this.method = method;
 556  89
             this.stepWithoutStartingWord = stepWithoutStartingWord;
 557  89
             this.namedParameters = namedParameters;
 558  89
         }
 559  
 
 560  
         public void describeTo(StoryReporter storyReporter) {
 561  4
             storyReporter.beforeStep(stepAsString);
 562  4
         }
 563  
 
 564  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 565  
             try {
 566  87
                 parametriseStep();
 567  84
                 stepMonitor.performing(parametrisedStep, dryRun);
 568  84
                 if (!dryRun) {
 569  80
                     method.invoke(stepsInstance(), convertedParameters);
 570  
                 }
 571  79
                 return successful(stepAsString).withParameterValues(parametrisedStep);
 572  1
             } catch (ParameterNotFound e) {
 573  
                 // step parametrisation failed, return pending StepResult
 574  1
                 return pending(stepAsString).withParameterValues(parametrisedStep);
 575  5
             } catch (InvocationTargetException e) {
 576  5
                 if (e.getCause() instanceof RestartingScenarioFailure) {
 577  1
                     throw (RestartingScenarioFailure) e.getCause();
 578  
                 }
 579  4
                 Throwable failureCause = e.getCause();
 580  4
                 if (failureCause instanceof UUIDExceptionWrapper) {
 581  1
                     failureCause = failureCause.getCause();
 582  
                 }
 583  4
                 return failed(stepAsString, new UUIDExceptionWrapper(stepAsString, failureCause)).withParameterValues(
 584  
                         parametrisedStep);
 585  2
             } catch (Throwable t) {
 586  2
                 return failed(stepAsString, new UUIDExceptionWrapper(stepAsString, t)).withParameterValues(
 587  
                         parametrisedStep);
 588  
             }
 589  
         }
 590  
 
 591  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 592  
             try {
 593  1
                 parametriseStep();
 594  
                 // } catch (ParameterNotFound e) {
 595  1
             } catch (Throwable t) {
 596  
                 // step parametrisation failed, but still return
 597  
                 // notPerformed StepResult
 598  0
             }
 599  1
             return notPerformed(stepAsString).withParameterValues(parametrisedStep);
 600  
         }
 601  
 
 602  
                 public String asString(Keywords keywords) {
 603  0
                         if ( parametrisedStep == null){
 604  0
                                 parametriseStep();
 605  
                         }
 606  0
                 return parametrisedStep;
 607  
         }
 608  
         
 609  
         private void parametriseStep() {
 610  88
             stepMatcher.find(stepWithoutStartingWord);
 611  86
             ParameterName[] names = parameterNames(method);
 612  86
             Type[] types = method.getGenericParameterTypes();
 613  86
             String[] parameterValues = parameterValuesForStep(namedParameters, types, names);
 614  84
             convertedParameters = convertParameterValues(parameterValues, types);
 615  84
             addNamedParametersToExamplesTables();
 616  84
             parametrisedStep = parametrisedStep(stepAsString, namedParameters, types, names, parameterValues);
 617  84
         }
 618  
 
 619  
         private void addNamedParametersToExamplesTables() {
 620  153
             for (Object object : convertedParameters) {
 621  69
                 if (object instanceof ExamplesTable) {
 622  0
                     ((ExamplesTable) object).withNamedParameters(namedParameters);
 623  
                 }
 624  
             }
 625  84
         }
 626  
 
 627  
     }
 628  
 
 629  
     public static class PendingStep extends AbstractStep {
 630  
         private final String stepAsString;
 631  
         private final String previousNonAndStep;
 632  
         private Method method;
 633  
 
 634  28
         public PendingStep(String stepAsString, String previousNonAndStep) {
 635  28
             this.stepAsString = stepAsString;
 636  28
             this.previousNonAndStep = previousNonAndStep;
 637  28
         }
 638  
 
 639  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 640  12
             return pending(stepAsString);
 641  
         }
 642  
 
 643  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 644  1
             return pending(stepAsString);
 645  
         }
 646  
 
 647  
         public String stepAsString() {
 648  13
             return stepAsString;
 649  
         }
 650  
 
 651  
         public String previousNonAndStepAsString() {
 652  13
             return previousNonAndStep;
 653  
         }
 654  
 
 655  
         public void annotatedOn(Method method) {
 656  0
             this.method = method;
 657  0
         }
 658  
 
 659  
         public boolean annotated() {
 660  0
             return method != null;
 661  
         }
 662  
 
 663  
                 public String asString(Keywords keywords) {
 664  0
                         return stepAsString;
 665  
                 }
 666  
 
 667  
     }
 668  
 
 669  
     public static class IgnorableStep extends AbstractStep {
 670  
         private final String stepAsString;
 671  
 
 672  2
         public IgnorableStep(String stepAsString) {
 673  2
             this.stepAsString = stepAsString;
 674  2
         }
 675  
 
 676  
         public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
 677  2
             return ignorable(stepAsString);
 678  
         }
 679  
 
 680  
         public StepResult doNotPerform(UUIDExceptionWrapper storyFailureIfItHappened) {
 681  1
             return ignorable(stepAsString);
 682  
         }
 683  
         
 684  
                 public String asString(Keywords keywords) {
 685  0
                         return stepAsString;
 686  
                 }
 687  
 
 688  
     }
 689  
 
 690  
     private class MethodInvoker {
 691  
         private final Method method;
 692  
         private final ParameterConverters parameterConverters;
 693  
         private final Paranamer paranamer;
 694  
         private final Meta meta;
 695  
         private int methodArity;
 696  
 
 697  39
         public MethodInvoker(Method method, ParameterConverters parameterConverters, Paranamer paranamer, Meta meta) {
 698  39
             this.method = method;
 699  39
             this.parameterConverters = parameterConverters;
 700  39
             this.paranamer = paranamer;
 701  39
             this.meta = meta;
 702  39
             this.methodArity = method.getParameterTypes().length;
 703  39
         }
 704  
 
 705  
         public void invoke() throws InvocationTargetException, IllegalAccessException {
 706  39
             method.invoke(stepsInstance(), parameterValuesFrom(meta));
 707  35
         }
 708  
 
 709  
         private Parameter[] methodParameters() {
 710  39
             Parameter[] parameters = new Parameter[methodArity];
 711  39
             String[] annotationNamedParameters = annotatedParameterNames(method);
 712  39
             String[] parameterNames = paranamer.lookupParameterNames(method, false);
 713  39
             Class<?>[] parameterTypes = method.getParameterTypes();
 714  
 
 715  63
             for (int paramPosition = 0; paramPosition < methodArity; paramPosition++) {
 716  24
                 String paramName = parameterNameFor(paramPosition, annotationNamedParameters, parameterNames);
 717  24
                 parameters[paramPosition] = new Parameter(paramPosition, parameterTypes[paramPosition], paramName);
 718  
             }
 719  
 
 720  39
             return parameters;
 721  
         }
 722  
 
 723  
         private String parameterNameFor(int paramPosition, String[] annotationNamedParameters, String[] parameterNames) {
 724  24
             String nameFromAnnotation = nameIfValidPositionInArray(annotationNamedParameters, paramPosition);
 725  24
             String parameterName = nameIfValidPositionInArray(parameterNames, paramPosition);
 726  24
             if (nameFromAnnotation != null) {
 727  16
                 return nameFromAnnotation;
 728  8
             } else if (parameterName != null) {
 729  2
                 return parameterName;
 730  
             }
 731  6
             return null;
 732  
         }
 733  
 
 734  
         private String nameIfValidPositionInArray(String[] paramNames, int paramPosition) {
 735  48
             return paramPosition < paramNames.length ? paramNames[paramPosition] : null;
 736  
         }
 737  
 
 738  
         private Object[] parameterValuesFrom(Meta meta) {
 739  39
             Object[] values = new Object[methodArity];
 740  63
             for (Parameter parameter : methodParameters()) {
 741  24
                 values[parameter.position] = parameterConverters.convert(parameter.valueFrom(meta), parameter.type);
 742  
             }
 743  39
             return values;
 744  
         }
 745  
 
 746  48
         private class Parameter {
 747  
             private final int position;
 748  
             private final Class<?> type;
 749  
             private final String name;
 750  
 
 751  24
             public Parameter(int position, Class<?> type, String name) {
 752  24
                 this.position = position;
 753  24
                 this.type = type;
 754  24
                 this.name = name;
 755  24
             }
 756  
 
 757  
             public String valueFrom(Meta meta) {
 758  24
                 if (name == null) {
 759  6
                     return null;
 760  
                 }
 761  18
                 return meta.getProperty(name);
 762  
             }
 763  
         }
 764  
     }
 765  
 
 766  342
     private static class ParameterName {
 767  
         private String name;
 768  
         private boolean annotated;
 769  
 
 770  76
         private ParameterName(String name, boolean annotated) {
 771  76
             this.name = name;
 772  76
             this.annotated = annotated;
 773  76
         }
 774  
     }
 775  
 
 776  
 }