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