Coverage Report - org.jbehave.core.steps.StepFinder
 
Classes in this File Line Coverage Branch Coverage Complexity
StepFinder
100%
24/24
91%
11/12
2.125
StepFinder$1
N/A
N/A
2.125
StepFinder$ByLevenshteinDistance
100%
5/5
N/A
2.125
StepFinder$ByLevenshteinDistance$1
100%
10/10
100%
2/2
2.125
StepFinder$ByLevenshteinDistance$LevenshteinDistance
92%
25/27
88%
16/18
2.125
StepFinder$ByPriorityField
100%
3/3
N/A
2.125
StepFinder$ByPriorityField$1
100%
2/2
N/A
2.125
StepFinder$PrioritisingStrategy
N/A
N/A
2.125
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.Collections;
 5  
 import java.util.Comparator;
 6  
 import java.util.LinkedList;
 7  
 import java.util.List;
 8  
 
 9  
 import org.apache.commons.lang.StringUtils;
 10  
 
 11  
 /**
 12  
  * <p>
 13  
  * StepFinder is reponsible for finding and prioritising step candidates or
 14  
  * finding steps instances from {@link CandidateSteps}, which are created using
 15  
  * an {@link InjectableStepsFactory}.
 16  
  * </p>
 17  
  * <p>
 18  
  * The {@link StepCandidate}s are responsible for the matching of a particular
 19  
  * textual step and are sometimes represented as {@link Stepdoc}s, each of which
 20  
  * is simply a facade documenting a candidate. The candidates can be prioritised
 21  
  * via an injectable {@link PrioritisingStrategy}, defaulting to
 22  
  * {@link ByPriorityField}. A more sophisticated strategy that can be used is
 23  
  * the {@link ByLevenshteinDistance}.
 24  
  * </p>
 25  
  */
 26  
 public class StepFinder {
 27  
 
 28  
     private PrioritisingStrategy prioritisingStrategy;
 29  
 
 30  
     /**
 31  
      * Creates a StepFinder with a {@link ByPriorityField} strategy
 32  
      */
 33  
     public StepFinder() {
 34  789
         this(new ByPriorityField());
 35  789
     }
 36  
 
 37  
     /**
 38  
      * Creates a StepFinder with a custom strategy
 39  
      * 
 40  
      * @param prioritisingStrategy
 41  
      *            the PrioritisingStrategy
 42  
      */
 43  790
     public StepFinder(PrioritisingStrategy prioritisingStrategy) {
 44  790
         this.prioritisingStrategy = prioritisingStrategy;
 45  790
     }
 46  
 
 47  
     /**
 48  
      * Returns the stepdocs for the candidates collected from the given
 49  
      * {@link CandidateSteps}.
 50  
      * 
 51  
      * @param candidateSteps
 52  
      *            the List of CandidateSteps
 53  
      * @return The List of Stepdocs, one for each {@link StepCandidate}.
 54  
      */
 55  
     public List<Stepdoc> stepdocs(List<CandidateSteps> candidateSteps) {
 56  2
         List<Stepdoc> stepdocs = new LinkedList<Stepdoc>();
 57  2
         for (StepCandidate candidate : collectCandidates(candidateSteps)) {
 58  6
             stepdocs.add(new Stepdoc(candidate));
 59  
         }
 60  2
         return stepdocs;
 61  
     }
 62  
 
 63  
     /**
 64  
      * Finds matching steps, represented as {@link Stepdoc}s, for a given
 65  
      * textual step and a list of {@link CandidateSteps}.
 66  
      * 
 67  
      * @param stepAsText
 68  
      *            the textual step
 69  
      * @param candidateSteps
 70  
      *            the List of CandidateSteps
 71  
      * @return The list of Stepdocs, one for each matched {@link StepCandidate}.
 72  
      */
 73  
     public List<Stepdoc> findMatching(String stepAsText, List<CandidateSteps> candidateSteps) {
 74  3
         List<Stepdoc> matching = new ArrayList<Stepdoc>();
 75  3
         for (StepCandidate candidate : collectCandidates(candidateSteps)) {
 76  6
             if (candidate.matches(stepAsText)) {
 77  1
                 matching.add(new Stepdoc(candidate));
 78  
             }
 79  
         }
 80  3
         return matching;
 81  
     }
 82  
 
 83  
     /**
 84  
      * Returns the steps instances associated to CandidateSteps
 85  
      * 
 86  
      * @param candidateSteps
 87  
      *            the List of CandidateSteps
 88  
      * @return The List of steps instances
 89  
      */
 90  
     public List<Object> stepsInstances(List<CandidateSteps> candidateSteps) {
 91  4
         List<Object> instances = new ArrayList<Object>();
 92  4
         for (CandidateSteps steps : candidateSteps) {
 93  3
             if (steps instanceof Steps) {
 94  3
                 instances.add(((Steps) steps).instance());
 95  
             }
 96  
         }
 97  4
         return instances;
 98  
     }
 99  
 
 100  
     /**
 101  
      * Collects a list of step candidates from {@link CandidateSteps} instances.
 102  
      * 
 103  
      * @param candidateSteps
 104  
      *            the list {@link CandidateSteps} instances
 105  
      * @return A List of {@link StepCandidate}
 106  
      */
 107  
     public List<StepCandidate> collectCandidates(List<CandidateSteps> candidateSteps) {
 108  12
         List<StepCandidate> collected = new ArrayList<StepCandidate>();
 109  12
         for (CandidateSteps steps : candidateSteps) {
 110  14
             collected.addAll(steps.listCandidates());
 111  
         }
 112  12
         return collected;
 113  
     }
 114  
 
 115  
     /**
 116  
      * Prioritises the list of step candidates that match a given step.
 117  
      * 
 118  
      * @param stepAsText
 119  
      *            the textual step to match
 120  
      * @param candidates
 121  
      *            the List of StepCandidate
 122  
      * @return The prioritised list according to the
 123  
      *         {@link PrioritisingStrategy}.
 124  
      */
 125  
     public List<StepCandidate> prioritise(String stepAsText, List<StepCandidate> candidates) {
 126  8
         return prioritisingStrategy.prioritise(stepAsText, candidates);
 127  
     }
 128  
 
 129  
     /**
 130  
      * Defines the priorising strategy of step candidates
 131  
      */
 132  
     public static interface PrioritisingStrategy {
 133  
 
 134  
         List<StepCandidate> prioritise(String stepAsString, List<StepCandidate> candidates);
 135  
 
 136  
     }
 137  
 
 138  
     /**
 139  
      * Strategy to priorise step candidates by the
 140  
      * {@link StepCandidate#getPriority()} field which is settable in the
 141  
      * {@link Given}, {@link When}, {@link Then} annotations.
 142  
      */
 143  789
     public static class ByPriorityField implements PrioritisingStrategy {
 144  
 
 145  
         public List<StepCandidate> prioritise(String stepAsText, List<StepCandidate> candidates) {
 146  7
             Collections.sort(candidates, new Comparator<StepCandidate>() {
 147  
                 public int compare(StepCandidate o1, StepCandidate o2) {
 148  8
                     return o2.getPriority().compareTo(o1.getPriority());
 149  
                 }
 150  
             });
 151  7
             return candidates;
 152  
         }
 153  
 
 154  
     }
 155  
 
 156  
     /**
 157  
      * Strategy to priorise candidate steps by <a
 158  
      * href="http://www.merriampark.com/ld.htm">Levenshtein Distance</a>
 159  
      */
 160  13
     public static class ByLevenshteinDistance implements PrioritisingStrategy {
 161  
 
 162  1
         private LevenshteinDistance ld = new LevenshteinDistance();
 163  
 
 164  
         public List<StepCandidate> prioritise(final String stepAsText, List<StepCandidate> candidates) {
 165  7
             Collections.sort(candidates, new Comparator<StepCandidate>() {
 166  
                 public int compare(StepCandidate o1, StepCandidate o2) {
 167  6
                     String scoringPattern1 = scoringPattern(o1);
 168  6
                     String scoringPattern2 = scoringPattern(o2);
 169  6
                     String stepWithoutStartingWord = trimStartingWord(stepAsText);
 170  6
                     Integer score1 = 0 - ld.calculate(scoringPattern1, stepWithoutStartingWord);
 171  6
                     Integer score2 = 0 - ld.calculate(scoringPattern2, stepWithoutStartingWord);
 172  6
                     int result = score2.compareTo(score1);
 173  
                     // default to strategy by priority if no score result
 174  6
                     return result != 0 ? result : o2.getPriority().compareTo(o1.getPriority());
 175  
                 }
 176  
 
 177  
                 private String scoringPattern(StepCandidate candidate) {
 178  12
                     return candidate.getPatternAsString().replaceAll("\\s\\$\\w+\\s", " ").replaceAll("\\$\\w+", "");
 179  
                 }
 180  
 
 181  
                 private String trimStartingWord(String stepAsString) {
 182  6
                     return StringUtils.substringAfter(stepAsString, " ");
 183  
                 }
 184  
 
 185  
             });
 186  1
             return candidates;
 187  
         }
 188  
 
 189  2
         private class LevenshteinDistance {
 190  
 
 191  
             public int calculate(String s, String t) {
 192  
                 int d[][]; // matrix
 193  
                 int n; // length of s
 194  
                 int m; // length of t
 195  
                 int i; // iterates through s
 196  
                 int j; // iterates through t
 197  
                 char s_i; // ith character of s
 198  
                 char t_j; // jth character of t
 199  
                 int cost; // cost
 200  
 
 201  
                 // Step 1
 202  12
                 n = s.length();
 203  12
                 m = t.length();
 204  12
                 if (n == 0) {
 205  0
                     return m;
 206  
                 }
 207  12
                 if (m == 0) {
 208  0
                     return n;
 209  
                 }
 210  12
                 d = new int[n + 1][m + 1];
 211  
                 // Step 2
 212  255
                 for (i = 0; i <= n; i++) {
 213  243
                     d[i][0] = i;
 214  
                 }
 215  96
                 for (j = 0; j <= m; j++) {
 216  84
                     d[0][j] = j;
 217  
                 }
 218  
                 // Step 3
 219  243
                 for (i = 1; i <= n; i++) {
 220  231
                     s_i = s.charAt(i - 1);
 221  
                     // Step 4
 222  1617
                     for (j = 1; j <= m; j++) {
 223  1386
                         t_j = t.charAt(j - 1);
 224  
                         // Step 5
 225  1386
                         if (s_i == t_j) {
 226  84
                             cost = 0;
 227  
                         } else {
 228  1302
                             cost = 1;
 229  
                         }
 230  
                         // Step 6
 231  1386
                         d[i][j] = minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
 232  
                     }
 233  
                 }
 234  
                 // Step 7
 235  12
                 return d[n][m];
 236  
             }
 237  
 
 238  
             private int minimum(int a, int b, int c) {
 239  1386
                 int mi = a;
 240  1386
                 if (b < mi) {
 241  171
                     mi = b;
 242  
                 }
 243  1386
                 if (c < mi) {
 244  216
                     mi = c;
 245  
                 }
 246  1386
                 return mi;
 247  
             }
 248  
 
 249  
         }
 250  
 
 251  
     }
 252  
 
 253  
 }