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