Coverage Report - org.jbehave.core.steps.StepCreator
 
Classes in this File Line Coverage Branch Coverage Complexity
StepCreator
99%
117/118
93%
58/62
2.174
StepCreator$1
100%
3/3
N/A
2.174
StepCreator$2
100%
3/3
N/A
2.174
StepCreator$3
100%
3/3
N/A
2.174
StepCreator$4
100%
3/3
N/A
2.174
StepCreator$5
95%
23/24
100%
2/2
2.174
StepCreator$6
100%
3/3
N/A
2.174
StepCreator$7
100%
3/3
N/A
2.174
StepCreator$8
100%
1/1
N/A
2.174
StepCreator$BeforeOrAfter
100%
8/8
N/A
2.174
StepCreator$Jsr330Helper
100%
2/2
N/A
2.174
StepCreator$ParameterNotFound
100%
4/4
N/A
2.174
StepCreator$Skip
100%
2/2
N/A
2.174
StepCreator$StepRunner
N/A
N/A
2.174
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import static java.util.Arrays.asList;
 4  
 import static org.jbehave.core.steps.AbstractStepResult.failed;
 5  
 import static org.jbehave.core.steps.AbstractStepResult.ignorable;
 6  
 import static org.jbehave.core.steps.AbstractStepResult.notPerformed;
 7  
 import static org.jbehave.core.steps.AbstractStepResult.pending;
 8  
 import static org.jbehave.core.steps.AbstractStepResult.skipped;
 9  
 import static org.jbehave.core.steps.AbstractStepResult.successful;
 10  
 
 11  
 import java.lang.annotation.Annotation;
 12  
 import java.lang.reflect.InvocationTargetException;
 13  
 import java.lang.reflect.Method;
 14  
 import java.lang.reflect.Type;
 15  
 import java.util.Map;
 16  
 
 17  
 import org.jbehave.core.annotations.Named;
 18  
 import org.jbehave.core.annotations.AfterScenario.Outcome;
 19  
 import org.jbehave.core.failures.BeforeOrAfterFailed;
 20  
 import org.jbehave.core.parsers.StepMatcher;
 21  
 
 22  
 import com.thoughtworks.paranamer.NullParanamer;
 23  
 import com.thoughtworks.paranamer.Paranamer;
 24  
 
 25  590
 public class StepCreator {
 26  
 
 27  
     public static final String PARAMETER_NAME_START = "<";
 28  
     public static final String PARAMETER_NAME_END = ">";
 29  
     public static final String PARAMETER_VALUE_START = "\uFF5F";
 30  
     public static final String PARAMETER_VALUE_END = "\uFF60";
 31  
     public static final String PARAMETER_VALUE_NEWLINE = "\u2424";
 32  
     private final Object stepsInstance;
 33  
     private final ParameterConverters parameterConverters;
 34  
     private final StepMatcher stepMatcher;
 35  
     private final StepRunner beforeOrAfter;
 36  
     private final StepRunner skip;
 37  
     private StepMonitor stepMonitor;
 38  99
     private Paranamer paranamer = new NullParanamer();
 39  99
     private boolean dryRun = false;
 40  
 
 41  
     public StepCreator(Object stepsInstance, StepMonitor stepMonitor) {
 42  22
         this(stepsInstance, null, null, stepMonitor);
 43  22
     }
 44  
 
 45  
     public StepCreator(Object stepsInstance, ParameterConverters parameterConverters, StepMatcher stepMatcher,
 46  99
             StepMonitor stepMonitor) {
 47  99
         this.stepsInstance = stepsInstance;
 48  99
         this.parameterConverters = parameterConverters;
 49  99
         this.stepMatcher = stepMatcher;
 50  99
         this.stepMonitor = stepMonitor;
 51  99
         this.beforeOrAfter = new BeforeOrAfter();
 52  99
         this.skip = new Skip();
 53  99
     }
 54  
 
 55  
     public void useStepMonitor(StepMonitor stepMonitor) {
 56  48
         this.stepMonitor = stepMonitor;
 57  48
     }
 58  
 
 59  
     public void useParanamer(Paranamer paranamer) {
 60  50
         this.paranamer = paranamer;
 61  50
     }
 62  
 
 63  
     public void doDryRun(boolean dryRun) {
 64  47
         this.dryRun = dryRun;
 65  47
     }
 66  
 
 67  
     public Step createBeforeOrAfterStep(final Method method) {
 68  12
         return new Step() {
 69  
             public StepResult doNotPerform() {
 70  1
                 return beforeOrAfter.run(method);
 71  
             }
 72  
 
 73  
             public StepResult perform() {
 74  11
                 return beforeOrAfter.run(method);
 75  
             }
 76  
         };
 77  
     }
 78  
 
 79  
     public Step createAfterStepUponOutcome(final Method method, Outcome outcome) {
 80  6
         switch (outcome) {
 81  
         case ANY:
 82  
         default:
 83  2
             return new Step() {
 84  
 
 85  
                 public StepResult doNotPerform() {
 86  1
                     return beforeOrAfter.run(method);
 87  
                 }
 88  
 
 89  
                 public StepResult perform() {
 90  1
                     return beforeOrAfter.run(method);
 91  
                 }
 92  
 
 93  
             };
 94  
         case SUCCESS:
 95  2
             return new Step() {
 96  
 
 97  
                 public StepResult doNotPerform() {
 98  1
                     return skip.run(method);
 99  
                 }
 100  
 
 101  
                 public StepResult perform() {
 102  1
                     return beforeOrAfter.run(method);
 103  
                 }
 104  
 
 105  
             };
 106  
         case FAILURE:
 107  2
             return new Step() {
 108  
 
 109  
                 public StepResult doNotPerform() {
 110  1
                     return beforeOrAfter.run(method);
 111  
                 }
 112  
 
 113  
                 public StepResult perform() {
 114  1
                     return skip.run(method);
 115  
                 }
 116  
 
 117  
             };
 118  
         }
 119  
     }
 120  
 
 121  
     public Step createParametrisedStep(final Method method, final String stepAsString,
 122  
             final String stepWithoutStartingWord, final Map<String, String> tableRow) {
 123  58
         return new Step() {
 124  
             private Object[] convertedParameters;
 125  
             private String parametrisedStep;
 126  
 
 127  
             public StepResult perform() {
 128  
                 try {
 129  58
                     parametriseStep();
 130  55
                     stepMonitor.performing(stepAsString, dryRun);
 131  55
                     if (!dryRun) {
 132  51
                         method.invoke(stepsInstance, convertedParameters);
 133  
                     }
 134  54
                     return successful(stepAsString).withParameterValues(parametrisedStep);
 135  1
                 } catch (ParameterNotFound e) {
 136  
                     // step parametrisation failed, return pending StepResult
 137  1
                     return pending(stepAsString).withParameterValues(parametrisedStep);
 138  1
                 } catch (InvocationTargetException e) {
 139  1
                     return failed(stepAsString, e.getCause()).withParameterValues(parametrisedStep);
 140  2
                 } catch (Throwable t) {
 141  2
                     return failed(stepAsString, t).withParameterValues(parametrisedStep);
 142  
                 }
 143  
             }
 144  
 
 145  
             public StepResult doNotPerform() {
 146  
                 try {
 147  1
                     parametriseStep();
 148  1
                 } catch (ParameterNotFound e) {
 149  
                     // step parametrisation failed, but still return
 150  
                     // notPerformed StepResult
 151  0
                 }
 152  1
                 return notPerformed(stepAsString).withParameterValues(parametrisedStep);
 153  
             }
 154  
 
 155  
             private void parametriseStep() {
 156  59
                 stepMatcher.find(stepWithoutStartingWord);
 157  57
                 String[] annotationNames = annotatedParameterNames(method);
 158  57
                 String[] parameterNames = paranamer.lookupParameterNames(method, false);
 159  57
                 Type[] types = method.getGenericParameterTypes();
 160  57
                 String[] parameters = parametersForStep(tableRow, types, annotationNames, parameterNames);
 161  55
                 convertedParameters = convertParameters(parameters, types);
 162  55
                 parametrisedStep = parametrisedStep(stepAsString, tableRow, types, annotationNames, parameterNames,
 163  
                         parameters);
 164  55
             }
 165  
 
 166  
         };
 167  
     }
 168  
 
 169  
     /**
 170  
      * Extract annotated parameter names from the @Named parameter annotations
 171  
      * of the method
 172  
      * 
 173  
      * @param method
 174  
      *            the Method containing the annotations
 175  
      * @return An array of annotated parameter names, which <b>may</b> include
 176  
      *         <code>null</code> values for parameters that are not annotated
 177  
      */
 178  
     private String[] annotatedParameterNames(Method method) {
 179  57
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
 180  57
         String[] names = new String[parameterAnnotations.length];
 181  101
         for (int x = 0; x < parameterAnnotations.length; x++) {
 182  44
             Annotation[] annotations = parameterAnnotations[x];
 183  60
             for (Annotation annotation : annotations) {
 184  16
                 names[x] = annotationName(annotation);
 185  
             }
 186  
         }
 187  57
         return names;
 188  
     }
 189  
 
 190  
     private String annotationName(Annotation annotation) {
 191  16
         if (annotation.annotationType().isAssignableFrom(Named.class)) {
 192  10
             return ((Named) annotation).value();
 193  6
         } else if ("javax.inject.Named".equals(annotation.annotationType().getName())) {
 194  6
             return Jsr330Helper.getNamedValue(annotation);
 195  
         } else {
 196  0
             return null;
 197  
         }
 198  
     }
 199  
 
 200  
     private String parametrisedStep(String stepAsString, Map<String, String> tableRow, Type[] types,
 201  
             String[] annotationNames, String[] parameterNames, String[] parameters) {
 202  55
         String parametrisedStep = stepAsString;
 203  95
         for (int position = 0; position < types.length; position++) {
 204  40
             parametrisedStep = replaceParameterValuesInStep(parametrisedStep, position, annotationNames,
 205  
                     parameterNames, parameters, tableRow);
 206  
         }
 207  55
         return parametrisedStep;
 208  
     }
 209  
 
 210  
     private String replaceParameterValuesInStep(String stepText, int position, String[] annotationNames,
 211  
             String[] parameterNames, String[] parameters, Map<String, String> tableRow) {
 212  40
         int annotatedNamePosition = parameterPosition(annotationNames, position);
 213  40
         int parameterNamePosition = parameterPosition(parameterNames, position);
 214  40
         if (annotatedNamePosition != -1) {
 215  12
             stepText = replaceTableValue(stepText, tableRow, annotationNames[position]);
 216  28
         } else if (parameterNamePosition != -1) {
 217  6
             stepText = replaceTableValue(stepText, tableRow, parameterNames[position]);
 218  
         }
 219  40
         stepText = replaceParameterValue(stepText, position, parameters);
 220  40
         return stepText;
 221  
     }
 222  
 
 223  
     private String replaceParameterValue(String stepText, int position, String[] parameters) {
 224  40
         String value = parameters[position];
 225  40
         if (value != null) {
 226  40
             stepText = stepText.replace(value, PARAMETER_VALUE_START + value + PARAMETER_VALUE_END);
 227  40
             stepText = stepText.replace("\n", PARAMETER_VALUE_NEWLINE);
 228  
         }
 229  40
         return stepText;
 230  
     }
 231  
 
 232  
     private String replaceTableValue(String stepText, Map<String, String> tableRow, String name) {
 233  18
         String value = getTableValue(tableRow, name);
 234  18
         if (value != null) {
 235  6
             stepText = stepText.replace(PARAMETER_NAME_START + name + PARAMETER_NAME_END, PARAMETER_VALUE_START + value
 236  
                     + PARAMETER_VALUE_END);
 237  
         }
 238  18
         return stepText;
 239  
     }
 240  
 
 241  
     private String[] parametersForStep(Map<String, String> tableRow, Type[] types, String[] annotationNames,
 242  
             String[] parameterNames) {
 243  57
         final String[] parameters = new String[types.length];
 244  97
         for (int position = 0; position < types.length; position++) {
 245  42
             parameters[position] = parameterForPosition(position, annotationNames, parameterNames, tableRow);
 246  
         }
 247  55
         return parameters;
 248  
     }
 249  
 
 250  
     private Object[] convertParameters(String[] parametersAsString, Type[] types) {
 251  55
         final Object[] parameters = new Object[parametersAsString.length];
 252  95
         for (int position = 0; position < parametersAsString.length; position++) {
 253  40
             parameters[position] = parameterConverters.convert(parametersAsString[position], types[position]);
 254  
         }
 255  55
         return parameters;
 256  
     }
 257  
 
 258  
     private String parameterForPosition(int position, String[] annotationNames, String[] parameterNames,
 259  
             Map<String, String> tableRow) {
 260  42
         int annotatedNamePosition = parameterPosition(annotationNames, position);
 261  42
         int parameterNamePosition = parameterPosition(parameterNames, position);
 262  42
         String parameter = null;
 263  42
         if (annotatedNamePosition != -1 && isGroupName(annotationNames[position])) {
 264  8
             String name = annotationNames[position];
 265  8
             stepMonitor.usingAnnotatedNameForParameter(name, position);
 266  8
             parameter = matchedParameter(name);
 267  8
         } else if (parameterNamePosition != -1 && isGroupName(parameterNames[position])) {
 268  4
             String name = parameterNames[position];
 269  4
             stepMonitor.usingParameterNameForParameter(name, position);
 270  4
             parameter = matchedParameter(name);
 271  4
         } else if (annotatedNamePosition != -1 && isTableFieldName(tableRow, annotationNames[position])) {
 272  4
             String name = annotationNames[position];
 273  4
             stepMonitor.usingTableAnnotatedNameForParameter(name, position);
 274  4
             parameter = getTableValue(tableRow, name);
 275  4
         } else if (parameterNamePosition != -1 && isTableFieldName(tableRow, parameterNames[position])) {
 276  2
             String name = parameterNames[position];
 277  2
             stepMonitor.usingTableParameterNameForParameter(name, position);
 278  2
             parameter = getTableValue(tableRow, name);
 279  2
         } else {
 280  24
             stepMonitor.usingNaturalOrderForParameter(position);
 281  24
             parameter = matchedParameter(position);
 282  
         }
 283  40
         stepMonitor.foundParameter(parameter, position);
 284  40
         return parameter;
 285  
     }
 286  
 
 287  
     String matchedParameter(String name) {
 288  13
         String[] parameterNames = stepMatcher.parameterNames();
 289  19
         for (int i = 0; i < parameterNames.length; i++) {
 290  18
             String parameterName = parameterNames[i];
 291  18
             if (name.equals(parameterName)) {
 292  12
                 return matchedParameter(i);
 293  
             }
 294  
         }
 295  1
         throw new ParameterNotFound(name, parameterNames);
 296  
     }
 297  
 
 298  
     private String matchedParameter(int position) {
 299  36
         String[] parameterNames = stepMatcher.parameterNames();
 300  36
         int matchedPosition = position + 1;
 301  36
         if (matchedPosition <= parameterNames.length) {
 302  34
             return stepMatcher.parameter(matchedPosition);
 303  
         }
 304  2
         throw new ParameterNotFound(position, parameterNames);
 305  
     }
 306  
 
 307  
     private int parameterPosition(String[] names, int position) {
 308  164
         if (names.length == 0) {
 309  70
             return -1;
 310  
         }
 311  94
         String positionName = names[position];
 312  180
         for (int i = 0; i < names.length; i++) {
 313  124
             String name = names[i];
 314  124
             if (name != null && positionName.equals(name)) {
 315  38
                 return i;
 316  
             }
 317  
         }
 318  56
         return -1;
 319  
     }
 320  
 
 321  
     private boolean isGroupName(String name) {
 322  20
         String[] groupNames = stepMatcher.parameterNames();
 323  26
         for (String groupName : groupNames) {
 324  18
             if (name.equals(groupName)) {
 325  12
                 return true;
 326  
             }
 327  
         }
 328  8
         return false;
 329  
     }
 330  
 
 331  
     private String getTableValue(Map<String, String> tableRow, String name) {
 332  24
         return tableRow.get(name);
 333  
     }
 334  
 
 335  
     private boolean isTableFieldName(Map<String, String> tableRow, String name) {
 336  8
         return tableRow.get(name) != null;
 337  
     }
 338  
 
 339  
     public interface StepRunner {
 340  
 
 341  
         StepResult run(Method method);
 342  
 
 343  
     }
 344  
 
 345  198
     private class BeforeOrAfter implements StepRunner {
 346  
         public StepResult run(Method method) {
 347  
             try {
 348  16
                 method.invoke(stepsInstance);
 349  2
             } catch (InvocationTargetException e) {
 350  2
                 throw new BeforeOrAfterFailed(method, e.getCause());
 351  1
             } catch (Throwable t) {
 352  1
                 throw new BeforeOrAfterFailed(t);
 353  13
             }
 354  13
             return skipped();
 355  
         }
 356  
     }
 357  
 
 358  198
     private class Skip implements StepRunner {
 359  
         public StepResult run(Method method) {
 360  2
             return skipped();
 361  
         }
 362  
     }
 363  
 
 364  
     public static Step createPendingStep(final String stepAsString) {
 365  9
         return new Step() {
 366  
             public StepResult perform() {
 367  2
                 return pending(stepAsString);
 368  
             }
 369  
 
 370  
             public StepResult doNotPerform() {
 371  1
                 return pending(stepAsString);
 372  
             }
 373  
         };
 374  
     }
 375  
 
 376  
     public static Step createIgnorableStep(final String stepAsString) {
 377  2
         return new Step() {
 378  
             public StepResult perform() {
 379  2
                 return ignorable(stepAsString);
 380  
             }
 381  
 
 382  
             public StepResult doNotPerform() {
 383  1
                 return ignorable(stepAsString);
 384  
             }
 385  
         };
 386  
     }
 387  
 
 388  
     /**
 389  
      * This is a different class, because the @Inject jar may not be in the
 390  
      * classpath.
 391  
      */
 392  6
     public static class Jsr330Helper {
 393  
 
 394  
         private static String getNamedValue(Annotation annotation) {
 395  6
             return ((javax.inject.Named) annotation).value();
 396  
         }
 397  
 
 398  
     }
 399  
 
 400  
     @SuppressWarnings("serial")
 401  
     public static class ParameterNotFound extends RuntimeException {
 402  
 
 403  
         public ParameterNotFound(String name, String[] parameters) {
 404  1
             super("Parameter not found for name '" + name + "' amongst '" + asList(parameters) + "'");
 405  1
         }
 406  
 
 407  
         public ParameterNotFound(int position, String[] parameters) {
 408  2
             super("Parameter not found for position '" + position + "' amongst '" + asList(parameters) + "'");
 409  2
         }
 410  
     }
 411  
 
 412  
 }