##### Coverage Report - org.jbehave.core.steps.StepCreator

Classes in this File | Line Coverage | Branch Coverage | Complexity
StepCreator |
95% | 151/158
86% | 78/90
| 2.051282051282051;2.051
StepCreator$1 |
100% | 1/1
N/A | N/A
| 2.051282051282051;2.051
StepCreator$AbstractStep |
50% | 1/2
N/A | N/A
| 2.051282051282051;2.051
StepCreator$BeforeOrAfterStep |
86% | 13/15
N/A | N/A
| 2.051282051282051;2.051
StepCreator$BeforeOrAfterStep$UUIDExceptionWrapperInjector |
100% | 5/5
100% | 2/2
| 2.051282051282051;2.051
StepCreator$FailureStep |
100% | 5/5
N/A | N/A
| 2.051282051282051;2.051
StepCreator$IgnorableStep |
100% | 5/5
N/A | N/A
| 2.051282051282051;2.051
StepCreator$Jsr330Helper |
100% | 2/2
N/A | N/A
| 2.051282051282051;2.051
StepCreator$MethodInvoker |
100% | 29/29
100% | 10/10
| 2.051282051282051;2.051
StepCreator$MethodInvoker$Parameter |
100% | 9/9
100% | 2/2
| 2.051282051282051;2.051
StepCreator$ParameterName |
100% | 5/5
N/A | N/A
| 2.051282051282051;2.051
StepCreator$ParameterNotFound |
100% | 4/4
N/A | N/A
| 2.051282051282051;2.051
StepCreator$ParameterisedStep |
95% | 38/40
90% | 9/10
| 2.051282051282051;2.051
StepCreator$PendingStep |
72% | 8/11
0% | 0/2
| 2.051282051282051;2.051
StepCreator$SuccessStep |
100% | 5/5
N/A | N/A
| 2.051282051282051;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 | 775 |
     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 | 178 |
         private Paranamer paranamer = new NullParanamer();
54 | 178 |
         private boolean dryRun = false;
55 |  |
56 |  |
         public StepCreator(Class<?> stepsType, InjectableStepsFactory stepsFactory,
57 |  |
                 ParameterConverters parameterConverters, ParameterControls parameterControls, StepMatcher stepMatcher,
58 | 178 |
                 StepMonitor stepMonitor) {
59 | 178 |
             this.stepsType = stepsType;
60 | 178 |
             this.stepsFactory = stepsFactory;
61 | 178 |
             this.parameterConverters = parameterConverters;
62 | 178 |
             this.parameterControls = parameterControls;
63 | 178 |
             this.stepMatcher = stepMatcher;
64 | 178 |
             this.stepMonitor = stepMonitor;
65 | 178 |
             this.delimitedNamePattern = Pattern.compile(parameterControls.nameDelimiterLeft() + "(\\w+?)"
66 |  |
                     + parameterControls.nameDelimiterRight());
67 | 178 |
         }
68 |  |
69 |  |
         public void useStepMonitor(StepMonitor stepMonitor) {
70 | 75 |
             this.stepMonitor = stepMonitor;
71 | 75 |
         }
72 |  |
73 |  |
         public void useParanamer(Paranamer paranamer) {
74 | 79 |
             this.paranamer = paranamer;
75 | 79 |
         }
76 |  |
77 |  |
         public void doDryRun(boolean dryRun) {
78 | 74 |
             this.dryRun = dryRun;
79 | 74 |
         }
80 |  |
81 |  |
         public Object stepsInstance() {
82 | 115 |
             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 | 88 |
             String[] annotatedNames = annotatedParameterNames(method);
133 | 88 |
             String[] paranamerNames = paranamerParameterNames(method);
134 |  |
135 | 88 |
             ParameterName[] parameterNames = new ParameterName[annotatedNames.length];
136 | 164 |
             for (int i = 0; i < annotatedNames.length; i++) {
137 | 76 |
                 parameterNames[i] = parameterName(annotatedNames, paranamerNames, i);
138 |  |
             }
139 | 88 |
             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 | 125 |
             Annotation[][] parameterAnnotations = method.getParameterAnnotations();
161 | 125 |
             String[] names = new String[parameterAnnotations.length];
162 | 225 |
             for (int i = 0; i < parameterAnnotations.length; i++) {
163 | 159 |
                 for (Annotation annotation : parameterAnnotations[i]) {
164 | 59 |
                     names[i] = annotationName(annotation);
165 |  |
                 }
166 |  |
             }
167 | 125 |
             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 | 88 |
             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 | 87 |
             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 | 82 |
                 String parametrisedStep = stepAsString;
207 |  |
                 // mark parameter values that are parsed
208 | 151 |
             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 | 82 |
             for (String name : namedParameters.keySet()) {
213 | 30 |
                 parametrisedStep = markNamedParameterValue(parametrisedStep, namedParameters, name);
214 |  |
             }
215 |  |
216 | 82 |
             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 | 88 |
             final String[] parameters = new String[types.length];
268 | 160 |
             for (int position = 0; position < types.length; position++) {
269 | 74 |
                 parameters[position] = parameterForPosition(position, names, namedParameters);
270 |  |
             }
271 | 86 |
             return parameters;
272 |  |
         }
273 |  |
274 |  |
         private Object[] convertParameterValues(String[] valuesAsString, Type[] types) {
275 | 82 |
             final Object[] parameters = new Object[valuesAsString.length];
276 | 151 |
             for (int position = 0; position < valuesAsString.length; position++) {
277 | 69 |
                 parameters[position] = parameterConverters.convert(valuesAsString[position], types[position]);
278 |  |
             }
279 | 82 |
             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 | 26 |
             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 | 171 |
         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 | 87 |
                     Map<String, String> namedParameters) {
535 | 87 |
                 this.stepAsString = stepAsString;
536 | 87 |
                 this.method = method;
537 | 87 |
                 this.stepWithoutStartingWord = stepWithoutStartingWord;
538 | 87 |
                 this.namedParameters = namedParameters;
539 | 87 |
             }
540 |  |
541 |  |
             public void describeTo(StoryReporter storyReporter) {
542 | 2 |
                 storyReporter.beforeStep(stepAsString);
543 | 2 |
             }
544 |  |
545 |  |
             public StepResult perform(UUIDExceptionWrapper storyFailureIfItHappened) {
546 |  |
                 try {
547 | 85 |
                     parametriseStep();
548 | 82 |
                     stepMonitor.performing(parametrisedStep, dryRun);
549 | 82 |
                     if (!dryRun) {
550 | 78 |
                         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 | 3 |
                 } catch (InvocationTargetException e) {
557 | 3 |
                     if (e.getCause() instanceof RestartingScenarioFailure) {
558 | 1 |
                         throw (RestartingScenarioFailure) e.getCause();
559 |  |
                     }
560 | 2 |
                     Throwable failureCause = e.getCause();
561 | 2 |
                     if (failureCause instanceof UUIDExceptionWrapper) {
562 | 1 |
                         failureCause = failureCause.getCause();
563 |  |
                     }
564 | 2 |
                     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 | 86 |
                 stepMatcher.find(stepWithoutStartingWord);
585 | 84 |
                 ParameterName[] names = parameterNames(method);
586 | 84 |
                 Type[] types = method.getGenericParameterTypes();
587 | 84 |
                 String[] parameterValues = parameterValuesForStep(namedParameters, types, names);
588 | 82 |
                 convertedParameters = convertParameterValues(parameterValues, types);
589 | 82 |
                 addNamedParametersToExamplesTables();
590 | 82 |
                 parametrisedStep = parametrisedStep(stepAsString, namedParameters, types, names, parameterValues);
591 | 82 |
             }
592 |  |
593 |  |
             private void addNamedParametersToExamplesTables() {
594 | 151 |
                 for (Object object : convertedParameters) {
595 | 69 |
                     if (object instanceof ExamplesTable) {
596 | 0 |
                         ((ExamplesTable) object).withNamedParameters(namedParameters);
597 |  |
                     }
598 |  |
                 }
599 | 82 |
             }
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 | 26 |
             public PendingStep(String stepAsString, String previousNonAndStep) {
609 | 26 |
                 this.stepAsString = stepAsString;
610 | 26 |
                 this.previousNonAndStep = previousNonAndStep;
611 | 26 |
             }
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 |  |
     }