Coverage Report - org.jbehave.core.steps.StepCandidate
 
Classes in this File Line Coverage Branch Coverage Complexity
StepCandidate
97%
75/77
96%
25/26
1.552
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import java.lang.reflect.Method;
 4  
 import java.util.List;
 5  
 import java.util.Map;
 6  
 
 7  
 import org.apache.commons.lang.StringUtils;
 8  
 import org.jbehave.core.annotations.AfterScenario.Outcome;
 9  
 import org.jbehave.core.annotations.Given;
 10  
 import org.jbehave.core.annotations.Pending;
 11  
 import org.jbehave.core.annotations.Then;
 12  
 import org.jbehave.core.annotations.When;
 13  
 import org.jbehave.core.configuration.Keywords;
 14  
 import org.jbehave.core.configuration.Keywords.StartingWordNotFound;
 15  
 import org.jbehave.core.parsers.StepMatcher;
 16  
 import org.jbehave.core.parsers.StepPatternParser;
 17  
 
 18  
 import com.thoughtworks.paranamer.Paranamer;
 19  
 
 20  
 /**
 21  
  * A StepCandidate is associated to a Java method annotated with {@link Given},
 22  
  * {@link When}, {@link Then} in a steps instance class. The StepCandidate is
 23  
  * responsible for matching the textual step against the pattern contained in
 24  
  * the method annotation via the {@link StepMatcher} and for the creation of the
 25  
  * matched executable step via the {@link StepCreator}.
 26  
  */
 27  
 public class StepCandidate {
 28  
 
 29  
     private final String patternAsString;
 30  
     private final Integer priority;
 31  
     private final StepType stepType;
 32  
     private final Method method;
 33  
     private final Class<?> stepsType;
 34  
     private final InjectableStepsFactory stepsFactory;
 35  
     private final Keywords keywords;
 36  
     private final StepMatcher stepMatcher;
 37  
     private final StepCreator stepCreator;
 38  
     private String[] composedSteps;
 39  333
     private StepMonitor stepMonitor = new SilentStepMonitor();
 40  
 
 41  
     public StepCandidate(String patternAsString, int priority, StepType stepType, Method method, Class<?> stepsType,
 42  
             InjectableStepsFactory stepsFactory, Keywords keywords, StepPatternParser stepPatternParser,
 43  333
             ParameterConverters parameterConverters, ParameterControls parameterControls) {
 44  333
         this.patternAsString = patternAsString;
 45  333
         this.priority = priority;
 46  333
         this.stepType = stepType;
 47  333
         this.method = method;
 48  333
         this.stepsType = stepsType;
 49  333
         this.stepsFactory = stepsFactory;
 50  333
         this.keywords = keywords;
 51  333
         this.stepMatcher = stepPatternParser.parseStep(stepType, patternAsString);
 52  333
         this.stepCreator = new StepCreator(stepsType, stepsFactory, parameterConverters, parameterControls,
 53  
                 stepMatcher, stepMonitor);
 54  333
     }
 55  
 
 56  
     public Method getMethod() {
 57  7
         return method;
 58  
     }
 59  
 
 60  
     public Integer getPriority() {
 61  1
         return priority;
 62  
     }
 63  
 
 64  
     public String getPatternAsString() {
 65  114
         return patternAsString;
 66  
     }
 67  
 
 68  
     public Object getStepsInstance() {
 69  7
         return stepsFactory.createInstanceOfType(stepsType);
 70  
     }
 71  
 
 72  
     public Class<?> getStepsType() {
 73  0
         return stepsType;
 74  
     }
 75  
     
 76  
     public StepType getStepType() {
 77  145
         return stepType;
 78  
     }
 79  
 
 80  
     public String getStartingWord() {
 81  76
         return keywords.startingWordFor(stepType);
 82  
     }
 83  
 
 84  
     public void useStepMonitor(StepMonitor stepMonitor) {
 85  359
         this.stepMonitor = stepMonitor;
 86  359
         this.stepCreator.useStepMonitor(stepMonitor);
 87  359
     }
 88  
 
 89  
     public void doDryRun(boolean dryRun) {
 90  302
         this.stepCreator.doDryRun(dryRun);
 91  302
     }
 92  
 
 93  
     public void useParanamer(Paranamer paranamer) {
 94  305
         this.stepCreator.useParanamer(paranamer);
 95  305
     }
 96  
 
 97  
     public void composedOf(String[] steps) {
 98  8
         this.composedSteps = steps;
 99  8
     }
 100  
 
 101  
     public boolean isComposite() {
 102  76
         return composedSteps != null && composedSteps.length > 0;
 103  
     }
 104  
 
 105  
     public String[] composedSteps() {
 106  3
         return composedSteps;
 107  
     }
 108  
 
 109  
     public boolean ignore(String stepAsString) {
 110  
         try {
 111  59
             String ignoreWord = keywords.startingWordFor(StepType.IGNORABLE);
 112  58
             return keywords.stepStartsWithWord(stepAsString, ignoreWord);
 113  1
         } catch (StartingWordNotFound e) {
 114  1
             return false;
 115  
         }
 116  
     }
 117  
 
 118  
     public boolean isPending() {
 119  59
         return method.isAnnotationPresent(Pending.class);
 120  
     }
 121  
 
 122  
     public boolean matches(String stepAsString) {
 123  95
         return matches(stepAsString, null);
 124  
     }
 125  
 
 126  
     public boolean matches(String step, String previousNonAndStep) {
 127  
         try {
 128  117
             boolean matchesType = true;
 129  117
             if (isAndStep(step)) {
 130  2
                 if (previousNonAndStep == null) {
 131  
                     // cannot handle AND step with no previous step
 132  1
                     matchesType = false;
 133  
                 } else {
 134  
                     // previous step type should match candidate step type
 135  1
                     matchesType = keywords.startingWordFor(stepType).equals(findStartingWord(previousNonAndStep));
 136  
                 }
 137  
             }
 138  116
             stepMonitor.stepMatchesType(step, previousNonAndStep, matchesType, stepType, method, stepsType);
 139  116
             boolean matchesPattern = stepMatcher.matches(stripStartingWord(step));
 140  110
             stepMonitor.stepMatchesPattern(step, matchesPattern, stepMatcher.pattern(), method, stepsType);
 141  
             // must match both type and pattern
 142  110
             return matchesType && matchesPattern;
 143  7
         } catch (StartingWordNotFound e) {
 144  7
             return false;
 145  
         }
 146  
     }
 147  
 
 148  
     public Step createMatchedStep(String stepAsString, Map<String, String> namedParameters) {
 149  132
         return stepCreator.createParametrisedStep(method, stepAsString, stripStartingWord(stepAsString),
 150  
                 namedParameters);
 151  
     }
 152  
 
 153  
     public Step createMatchedStepUponOutcome(String stepAsString, Map<String, String> namedParameters, Outcome outcome) {
 154  0
         return stepCreator.createParametrisedStepUponOutcome(method, stepAsString, stripStartingWord(stepAsString),
 155  
                 namedParameters, outcome);
 156  
     }
 157  
 
 158  
     public void addComposedSteps(List<Step> steps, String stepAsString, Map<String, String> namedParameters,
 159  
             List<StepCandidate> allCandidates) {
 160  5
         addComposedStepsRecursively(steps, stepAsString, namedParameters, allCandidates, composedSteps);
 161  5
     }
 162  
 
 163  
     private void addComposedStepsRecursively(List<Step> steps, String stepAsString,
 164  
             Map<String, String> namedParameters, List<StepCandidate> allCandidates, String[] composedSteps) {
 165  16
         Map<String, String> matchedParameters = stepCreator.matchedParameters(method, stepAsString,
 166  8
                 stripStartingWord(stepAsString), namedParameters);
 167  8
         matchedParameters.putAll(namedParameters);
 168  25
         for (String composedStep : composedSteps) {
 169  17
             addComposedStep(steps, composedStep, matchedParameters, allCandidates);
 170  
         }
 171  8
     }
 172  
 
 173  
     private void addComposedStep(List<Step> steps, String composedStep, Map<String, String> matchedParameters,
 174  
             List<StepCandidate> allCandidates) {
 175  17
         StepCandidate candidate = findComposedCandidate(composedStep, allCandidates);
 176  17
         if (candidate != null) {
 177  14
             steps.add(candidate.createMatchedStep(composedStep, matchedParameters));
 178  14
             if (candidate.isComposite()) {
 179  
                 // candidate is itself composite: recursively add composed steps
 180  6
                 addComposedStepsRecursively(steps, composedStep, matchedParameters, allCandidates,
 181  3
                         candidate.composedSteps());
 182  
             }
 183  
         } else {
 184  3
             steps.add(StepCreator.createPendingStep(composedStep, null));
 185  
         }
 186  17
     }
 187  
 
 188  
     private StepCandidate findComposedCandidate(String composedStep, List<StepCandidate> allCandidates) {
 189  17
         for (StepCandidate candidate : allCandidates) {
 190  69
             if (StringUtils.startsWith(composedStep, candidate.getStartingWord())
 191  44
                     && (StringUtils.endsWith(composedStep, candidate.getPatternAsString()) || candidate
 192  32
                             .matches(composedStep))) {
 193  14
                 return candidate;
 194  
             }
 195  55
         }
 196  3
         return null;
 197  
     }
 198  
 
 199  
     public boolean isAndStep(String stepAsString) {
 200  174
         return keywords.isAndStep(stepAsString);
 201  
     }
 202  
 
 203  
     public boolean isIgnorableStep(String stepAsString) {
 204  57
         return keywords.isIgnorableStep(stepAsString);
 205  
     }
 206  
 
 207  
     private String findStartingWord(String stepAsString) {
 208  1
         return keywords.startingWord(stepAsString, stepType);
 209  
     }
 210  
 
 211  
     private String stripStartingWord(String stepAsString) {
 212  256
         return keywords.stepWithoutStartingWord(stepAsString, stepType);
 213  
     }
 214  
 
 215  
     @Override
 216  
     public String toString() {
 217  93
         return stepType + " " + patternAsString;
 218  
     }
 219  
 
 220  
 }