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