package cucumber.runtime; import cucumber.api.StepDefinitionReporter; import cucumber.runtime.xstream.LocalizedXStreams; import gherkin.I18n; import gherkin.formatter.Argument; import gherkin.formatter.model.Step; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; public class RuntimeGlue implements Glue { final Map<String, StepDefinition> stepDefinitionsByPattern = new TreeMap<String, StepDefinition>(); final List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>(); final List<HookDefinition> afterHooks = new ArrayList<HookDefinition>(); private final UndefinedStepsTracker tracker; private final LocalizedXStreams localizedXStreams; public RuntimeGlue(UndefinedStepsTracker tracker, LocalizedXStreams localizedXStreams) { this.tracker = tracker; this.localizedXStreams = localizedXStreams; } @Override public void addStepDefinition(StepDefinition stepDefinition) { StepDefinition previous = stepDefinitionsByPattern.get(stepDefinition.getPattern()); if (previous != null) { throw new DuplicateStepDefinitionException(previous, stepDefinition); } stepDefinitionsByPattern.put(stepDefinition.getPattern(), stepDefinition); } @Override public void addBeforeHook(HookDefinition hookDefinition) { beforeHooks.add(hookDefinition); Collections.sort(beforeHooks, new HookComparator(true)); } @Override public void addAfterHook(HookDefinition hookDefinition) { afterHooks.add(hookDefinition); Collections.sort(afterHooks, new HookComparator(false)); } @Override public List<HookDefinition> getBeforeHooks() { return beforeHooks; } @Override public List<HookDefinition> getAfterHooks() { return afterHooks; } @Override public StepDefinitionMatch stepDefinitionMatch(String featurePath, Step step, I18n i18n) { List<StepDefinitionMatch> matches = stepDefinitionMatches(featurePath, step); try { if (matches.isEmpty()) { tracker.addUndefinedStep(step, i18n); return null; } if (matches.size() == 1) { return matches.get(0); } else { throw new AmbiguousStepDefinitionsException(matches); } } finally { tracker.storeStepKeyword(step, i18n); } } private List<StepDefinitionMatch> stepDefinitionMatches(String featurePath, Step step) { List<StepDefinitionMatch> result = new ArrayList<StepDefinitionMatch>(); for (StepDefinition stepDefinition : stepDefinitionsByPattern.values()) { List<Argument> arguments = stepDefinition.matchedArguments(step); if (arguments != null) { result.add(new StepDefinitionMatch(arguments, stepDefinition, featurePath, step, localizedXStreams)); } } return result; } @Override public void reportStepDefinitions(StepDefinitionReporter stepDefinitionReporter) { for (StepDefinition stepDefinition : stepDefinitionsByPattern.values()) { stepDefinitionReporter.stepDefinition(stepDefinition); } } @Override public void removeScenarioScopedGlue() { removeScenarioScopedHooks(beforeHooks); removeScenarioScopedHooks(afterHooks); removeScenarioScopedStepdefs(); } private void removeScenarioScopedHooks(List<HookDefinition> beforeHooks1) { Iterator<HookDefinition> hookIterator = beforeHooks1.iterator(); while(hookIterator.hasNext()) { HookDefinition hook = hookIterator.next(); if(hook.isScenarioScoped()) { hookIterator.remove(); } } } private void removeScenarioScopedStepdefs() { Iterator<Map.Entry<String, StepDefinition>> stepdefs = stepDefinitionsByPattern.entrySet().iterator(); while(stepdefs.hasNext()) { StepDefinition stepDefinition = stepdefs.next().getValue(); if(stepDefinition.isScenarioScoped()) { stepdefs.remove(); } } } }