- Revision
- 1213
- Author
- mauro
- Date
- 2009-08-28 09:57:15 -0500 (Fri, 28 Aug 2009)
Log Message
JBEHAVE-173: Paired with Paul to implement usage of named parameters for step methods.
Modified Paths
- trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/scenarios/TraderSteps.java
- trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/parser/PrefixCapturingPatternBuilderBehaviour.java
- trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/steps/CandidateStepBehaviour.java
- trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/PrefixCapturingPatternBuilder.java
- trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/StepPatternBuilder.java
- trunk/core/jbehave-core/src/java/org/jbehave/scenario/steps/CandidateStep.java
Added Paths
Diff
Modified: trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/scenarios/TraderSteps.java (1212 => 1213)
--- trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/scenarios/TraderSteps.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/scenarios/TraderSteps.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -13,6 +13,7 @@ import org.jbehave.scenario.annotations.Alias; import org.jbehave.scenario.annotations.Aliases; import org.jbehave.scenario.annotations.Given; +import org.jbehave.scenario.annotations.Named; import org.jbehave.scenario.annotations.Then; import org.jbehave.scenario.annotations.When; import org.jbehave.scenario.parser.PrefixCapturingPatternBuilder; @@ -44,7 +45,8 @@ } @Given("a stock of prices %prices and a threshold of %threshold") - public void aStockOfPrice(List<Double> prices, double threshold) { + // Parameters are in reverse order to prove one of the features of @Named annotation + public void aStockOfPrice(@Named("threshold") double threshold, @Named("prices") List<Double> prices) { stock = new Stock(prices, threshold); }
Modified: trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/parser/PrefixCapturingPatternBuilderBehaviour.java (1212 => 1213)
--- trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/parser/PrefixCapturingPatternBuilderBehaviour.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/parser/PrefixCapturingPatternBuilderBehaviour.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -68,8 +68,16 @@ "The grid looks like ."); ensureThat(matched.matches()); ensureThat(matched.group(1), equalTo( - ".")); - + ".")); } + + @Test + public void shouldExtractParameterNamesFromStepPattern(){ + StepPatternBuilder builder = new PrefixCapturingPatternBuilder(); + String[] names = builder.extractParameterNames("The grid $name looks like $grid"); + ensureThat(names.length, equalTo(2)); + ensureThat(names[0], equalTo("name")); + ensureThat(names[1], equalTo("grid")); + } }
Modified: trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/steps/CandidateStepBehaviour.java (1212 => 1213)
--- trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/steps/CandidateStepBehaviour.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/jbehave-core/src/behaviour/org/jbehave/scenario/steps/CandidateStepBehaviour.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -7,8 +7,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.MethodDescriptor; +import java.lang.reflect.Method; import java.util.List; +import org.jbehave.scenario.annotations.Named; import org.jbehave.scenario.parser.PrefixCapturingPatternBuilder; import org.jbehave.scenario.parser.StepPatternBuilder; import org.jbehave.scenario.reporters.ScenarioReporter; @@ -136,4 +142,45 @@ candidateStep.createFrom("When windows on the 1,2,3 floors").perform(); ensureThat(((List<?>) someSteps.args).toString(), equalTo(asList("1", "2", "3").toString())); } + + @Test + public void shouldMatchMethodParametersByAnnotatedNames() throws Exception { + NamedParameterSteps steps = new NamedParameterSteps(); + CandidateStep candidateStep = new CandidateStep("I live on the $ith floor but some call it the $nth", + NamedParameterSteps.methodFor("methodWithNamedParametersInOrder"), steps, PATTERN_BUILDER, MONITOR, new ParameterConverters(), "Given", "When", "Then"); + candidateStep.createFrom("When I live on the first floor but some call it the ground").perform(); + ensureThat(steps.ith, equalTo("first")); + ensureThat(steps.nth, equalTo("ground")); + candidateStep = new CandidateStep("I live on the $ith floor but some call it the $nth", + NamedParameterSteps.methodFor("methodWithNamedParametersInInverseOrder"), steps, PATTERN_BUILDER, MONITOR, new ParameterConverters(), "Given", "When", "Then"); + candidateStep.createFrom("When I live on the first floor but some call it the ground").perform(); + ensureThat(steps.ith, equalTo("first")); + ensureThat(steps.nth, equalTo("ground")); + } + + public static class NamedParameterSteps extends Steps { + String ith; + String nth; + + public void methodWithNamedParametersInOrder(@Named("ith") String ithName, @Named("nth") String nthName){ + this.ith = ithName; + this.nth = nthName; + } + + public void methodWithNamedParametersInInverseOrder(@Named("nth") String nthName, @Named("ith") String ithName){ + this.ith = ithName; + this.nth = nthName; + } + + public static Method methodFor(String methodName) throws IntrospectionException { + BeanInfo beanInfo = Introspector.getBeanInfo(NamedParameterSteps.class); + for (MethodDescriptor md : beanInfo.getMethodDescriptors()) { + if (md.getMethod().getName().equals(methodName)) { + return md.getMethod(); + } + } + return null; + } + } + }
Added: trunk/core/jbehave-core/src/java/org/jbehave/scenario/annotations/Named.java (0 => 1213)
--- trunk/core/jbehave-core/src/java/org/jbehave/scenario/annotations/Named.java (rev 0) +++ trunk/core/jbehave-core/src/java/org/jbehave/scenario/annotations/Named.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -0,0 +1,14 @@ +package org.jbehave.scenario.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +...@retention(RetentionPolicy.RUNTIME) +...@target(ElementType.PARAMETER) +public @interface Named { + + String value(); + +} \ No newline at end of file
Modified: trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/PrefixCapturingPatternBuilder.java (1212 => 1213)
--- trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/PrefixCapturingPatternBuilder.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/PrefixCapturingPatternBuilder.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -14,6 +14,7 @@ */ public class PrefixCapturingPatternBuilder implements StepPatternBuilder { + private final String prefix; private final String anyWordBeginningWithThePrefix; /** @@ -29,7 +30,8 @@ * prefix in a matching step. */ public PrefixCapturingPatternBuilder(String prefix) { - anyWordBeginningWithThePrefix = "(\\" + prefix + "\\w*)(\\W|\\Z)"; + this.prefix = prefix; + this.anyWordBeginningWithThePrefix = "(\\" + prefix + "\\w*)(\\W|\\Z)"; } public Pattern buildPattern(String matchThis) { @@ -49,7 +51,7 @@ Matcher findingAllTheDollarWords = Pattern.compile(anyWordBeginningWithThePrefix, Pattern.DOTALL).matcher(matchThisButLeaveBrackets); List<Replacement> replacements = new ArrayList<Replacement>(); while(findingAllTheDollarWords.find()) { - replacements.add(new Replacement(findingAllTheDollarWords.start(), findingAllTheDollarWords.end(), findingAllTheDollarWords.group(2))); + replacements.add(new Replacement(matchThisButLeaveBrackets, findingAllTheDollarWords.start(), findingAllTheDollarWords.end(), findingAllTheDollarWords.group(2))); } return replacements; } @@ -71,16 +73,27 @@ return escapedMatch; } - private static class Replacement { + private class Replacement { private final int start; private final int end; private final String whitespaceIfAny; + private final String name; - public Replacement(int start, int end, String whitespaceIfAny) { + public Replacement(String pattern, int start, int end, String whitespaceIfAny) { this.start = start; this.end = end; this.whitespaceIfAny = whitespaceIfAny; + this.name = pattern.substring(start + prefix.length(), end).trim(); } + } + public String[] extractParameterNames(String pattern) { + List<String> names = new ArrayList<String>(); + for (Replacement replacement : findArgumentsToReplace(pattern)) { + names.add(replacement.name); + } + return names.toArray(new String[names.size()]); + } + }
Modified: trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/StepPatternBuilder.java (1212 => 1213)
--- trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/StepPatternBuilder.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/jbehave-core/src/java/org/jbehave/scenario/parser/StepPatternBuilder.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -23,4 +23,11 @@ */ Pattern buildPattern(String matchThis); + /** + * Extract the parameter names from a template step + * @param step the template step + * @return an array of parameter names + */ + String[] extractParameterNames(String string); + }
Modified: trunk/core/jbehave-core/src/java/org/jbehave/scenario/steps/CandidateStep.java (1212 => 1213)
--- trunk/core/jbehave-core/src/java/org/jbehave/scenario/steps/CandidateStep.java 2009-08-27 16:46:48 UTC (rev 1212) +++ trunk/core/jbehave-core/src/java/org/jbehave/scenario/steps/CandidateStep.java 2009-08-28 14:57:15 UTC (rev 1213) @@ -1,11 +1,13 @@ package org.jbehave.scenario.steps; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jbehave.scenario.annotations.Named; import org.jbehave.scenario.errors.PendingError; import org.jbehave.scenario.parser.StepPatternBuilder; @@ -24,6 +26,7 @@ private final String[] startingWords; private final Pattern pattern; private StepMonitor stepMonitor = new SilentStepMonitor(); + private String[] parameterNames; public CandidateStep(String stepAsString, Method method, CandidateSteps steps, StepPatternBuilder patterBuilder, StepMonitor stepMonitor, ParameterConverters parameterConverters, String... startingWords) { @@ -31,14 +34,15 @@ useStepMonitor(stepMonitor); } - public CandidateStep(String stepAsString, Method method, CandidateSteps steps, StepPatternBuilder patterBuilder, + public CandidateStep(String stepAsString, Method method, CandidateSteps steps, StepPatternBuilder patternBuilder, ParameterConverters parameterConverters, String... startingWords) { this.stepAsString = stepAsString; this.method = method; this.steps = steps; this.parameterConverters = parameterConverters; this.startingWords = startingWords; - this.pattern = patterBuilder.buildPattern(stepAsString); + this.pattern = patternBuilder.buildPattern(stepAsString); + this.parameterNames = patternBuilder.extractParameterNames(stepAsString); } public void useStepMonitor(StepMonitor stepMonitor) { @@ -66,16 +70,53 @@ Matcher matcher = pattern.matcher(trimStartingWord(startingWord, stepAsString)); matcher.find(); Type[] types = method.getGenericParameterTypes(); - final Object[] args = new Object[matcher.groupCount()]; + String[] annotatedParameterNames = annotatedParameterNames(); + int groupCount = matcher.groupCount(); + final Object[] args = new Object[groupCount]; for (int group = 0; group < args.length; group++) { - String arg = matcher.group(group + 1); + int parameterIndex = parameterIndex(annotatedParameterNames, group); + int groupIndex = group + 1; // default in case parameter index is not found + if ( parameterIndex != -1 ){ + groupIndex = parameterIndex + 1; + } + String arg = matcher.group(groupIndex); Object converted = parameterConverters.convert(arg, types[group]); args[group] = converted; } return createStep(stepAsString, args); } - private String findStartingWord(final String stepAsString) { + private int parameterIndex(String[] annotatedNames, int group) { + String name = parameterNames[group]; + for ( int index = 0; index < annotatedNames.length; index++ ){ + if ( name.equals(annotatedNames[index]) ){ + return index; + } + } + return -1; + } + + /** + * Extract annotated parameter names from the @Named parameter annotations + * @return An array of annotated parameter names, which <b>may</b> include <code>null</code> values + * for parameters that are not annotated + */ + private String[] annotatedParameterNames() { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + String[] names = new String[parameterAnnotations.length]; + for (int x = 0; x < parameterAnnotations.length; x ++) { + Annotation[] annotations = parameterAnnotations[x]; + for (int y = 0; y < annotations.length; y ++) { + Annotation annotation = annotations[y]; + if (annotation.annotationType().isAssignableFrom(Named.class) ){ + names[x] = ((Named)annotation).value(); + } + } + } + return names; + } + + private String findStartingWord(final String stepAsString) { for (String word : startingWords) { if (stepAsString.startsWith(word)) { return word;
To unsubscribe from this list please visit:
