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