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