Coverage Report - org.jbehave.core.steps.StepCandidate
 
Classes in this File Line Coverage Branch Coverage Complexity
StepCandidate
100%
59/59
100%
14/14
1.609
StepCandidate$StartingWordNotFound
100%
4/4
N/A
1.609
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import java.lang.reflect.Method;
 4  
 import java.util.Map;
 5  
 
 6  
 import org.jbehave.core.annotations.Given;
 7  
 import org.jbehave.core.annotations.Then;
 8  
 import org.jbehave.core.annotations.When;
 9  
 import org.jbehave.core.parsers.StepMatcher;
 10  
 import org.jbehave.core.parsers.StepPatternParser;
 11  
 
 12  
 import com.thoughtworks.paranamer.Paranamer;
 13  
 
 14  
 /**
 15  
  * A StepCandidate is associated to a Java method annotated with {@link Given},
 16  
  * {@link When}, {@link Then} in a steps instance class. The StepCandidate is
 17  
  * responsible for matching the textual step against the pattern contained in
 18  
  * the method annotation via the {@link StepMatcher} and for the creation of the
 19  
  * matched executable step via the {@link StepCreator}.
 20  
  */
 21  
 public class StepCandidate {
 22  
 
 23  
     private final String patternAsString;
 24  
     private final Integer priority;
 25  
     private final StepType stepType;
 26  
     private final Method method;
 27  
     private final Object stepsInstance;
 28  
     private final Map<StepType, String> startingWordsByType;
 29  
     private final StepMatcher stepMatcher;
 30  
     private final StepCreator stepCreator;
 31  76
     private StepMonitor stepMonitor = new SilentStepMonitor();
 32  
 
 33  
     public StepCandidate(String patternAsString, int priority, StepType stepType, Method method, Object stepsInstance,
 34  
             Map<StepType, String> startingWordsByType, StepPatternParser stepPatternParser,
 35  76
             ParameterConverters parameterConverters) {
 36  76
         this.patternAsString = patternAsString;
 37  76
         this.priority = priority;
 38  76
         this.stepType = stepType;
 39  76
         this.method = method;
 40  76
         this.stepsInstance = stepsInstance;
 41  76
         this.startingWordsByType = startingWordsByType;
 42  76
         this.stepMatcher = stepPatternParser.parseStep(patternAsString);
 43  76
         this.stepCreator = new StepCreator(stepsInstance, parameterConverters, stepMatcher, stepMonitor);
 44  76
     }
 45  
 
 46  
     public Method getMethod() {
 47  7
         return method;
 48  
     }
 49  
 
 50  
     public Integer getPriority() {
 51  1
         return priority;
 52  
     }
 53  
 
 54  
     public String getPatternAsString() {
 55  23
         return patternAsString;
 56  
     }
 57  
 
 58  
     public Object getStepsInstance() {
 59  7
         return stepsInstance;
 60  
     }
 61  
 
 62  
     public StepType getStepType() {
 63  95
         return stepType;
 64  
     }
 65  
 
 66  
     public String getStartingWord() {
 67  7
         return startingWordFor(stepType);
 68  
     }
 69  
 
 70  
     public void useStepMonitor(StepMonitor stepMonitor) {
 71  47
         this.stepMonitor = stepMonitor;
 72  47
         this.stepCreator.useStepMonitor(stepMonitor);
 73  47
     }
 74  
 
 75  
     public void doDryRun(boolean dryRun) {
 76  47
         this.stepCreator.doDryRun(dryRun);
 77  47
     }
 78  
 
 79  
     public void useParanamer(Paranamer paranamer) {
 80  50
         this.stepCreator.useParanamer(paranamer);
 81  50
     }
 82  
 
 83  
     public boolean ignore(String stepAsString) {
 84  
         try {
 85  2
             String ignoreWord = startingWordFor(StepType.IGNORABLE);
 86  1
             return stepStartsWithWord(stepAsString, ignoreWord);
 87  1
         } catch (StartingWordNotFound e) {
 88  1
             return false;
 89  
         }
 90  
     }
 91  
 
 92  
     public boolean matches(String stepAsString) {
 93  12
         return matches(stepAsString, null);
 94  
     }
 95  
 
 96  
     public boolean matches(String step, String previousNonAndStep) {
 97  
         try {
 98  13
             boolean matchesType = true;
 99  13
             if (isAndStep(step)) {
 100  2
                 if (previousNonAndStep == null) {
 101  
                     // cannot handle AND step with no previous step
 102  1
                     matchesType = false;
 103  
                 } else {
 104  
                     // previous step type should match candidate step type
 105  1
                     matchesType = startingWordFor(stepType).equals(findStartingWord(previousNonAndStep));
 106  
                 }
 107  
             }
 108  12
             stepMonitor.stepMatchesType(step, previousNonAndStep, matchesType, stepType, method, stepsInstance);
 109  12
             boolean matchesPattern = stepMatcher.matches(stripStartingWord(step));
 110  8
             stepMonitor.stepMatchesPattern(step, matchesPattern, stepMatcher.pattern(), method, stepsInstance);
 111  
             // must match both type and pattern
 112  8
             return matchesType && matchesPattern;
 113  5
         } catch (StartingWordNotFound e) {
 114  5
             return false;
 115  
         }
 116  
     }
 117  
 
 118  
     public boolean isAndStep(String stepAsString) {
 119  13
         String andWord = startingWordFor(StepType.AND);
 120  12
         return stepStartsWithWord(stepAsString, andWord);
 121  
     }
 122  
 
 123  
     public Step createMatchedStep(String stepAsString, Map<String, String> tableRow) {
 124  58
         return stepCreator.createParametrisedStep(method, stepAsString, stripStartingWord(stepAsString), tableRow);
 125  
     }
 126  
 
 127  
     private String stripStartingWord(final String stepAsString) {
 128  70
         String startingWord = findStartingWord(stepAsString);
 129  64
         return trimStartingWord(startingWord, stepAsString);
 130  
     }
 131  
 
 132  
     private String findStartingWord(final String stepAsString) throws StartingWordNotFound {
 133  71
         String wordForType = startingWordFor(stepType);
 134  71
         if (stepStartsWithWord(stepAsString, wordForType)) {
 135  59
             return wordForType;
 136  
         }
 137  12
         String andWord = startingWordFor(StepType.AND);
 138  12
         if (stepStartsWithWord(stepAsString, andWord)) {
 139  6
             return andWord;
 140  
         }
 141  6
         throw new StartingWordNotFound(stepAsString, stepType, startingWordsByType);
 142  
     }
 143  
 
 144  
     private boolean stepStartsWithWord(String step, String word) {
 145  96
         return step.startsWith(word + " "); // space after qualifies it as word
 146  
     }
 147  
 
 148  
     private String trimStartingWord(String word, String step) {
 149  64
         return step.substring(word.length() + 1); // 1 for the space after
 150  
     }
 151  
 
 152  
     private String startingWordFor(StepType stepType) {
 153  106
         String startingWord = startingWordsByType.get(stepType);
 154  106
         if (startingWord == null) {
 155  2
             throw new StartingWordNotFound(stepType, startingWordsByType);
 156  
         }
 157  104
         return startingWord;
 158  
     }
 159  
 
 160  
     @Override
 161  
     public String toString() {
 162  93
         return stepType + " " + patternAsString;
 163  
     }
 164  
 
 165  
     @SuppressWarnings("serial")
 166  
     public static class StartingWordNotFound extends RuntimeException {
 167  
 
 168  
         public StartingWordNotFound(String step, StepType stepType, Map<StepType, String> startingWordsByType) {
 169  6
             super("No starting word found for step '" + step + "' of type '" + stepType + "' amongst '"
 170  
                     + startingWordsByType + "'");
 171  6
         }
 172  
 
 173  
         public StartingWordNotFound(StepType stepType, Map<StepType, String> startingWordsByType) {
 174  2
             super("No starting word found of type '" + stepType + "' amongst '" + startingWordsByType + "'");
 175  2
         }
 176  
 
 177  
     }
 178  
 
 179  
 }