<commit_before>package org.jbehave.core.steps;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Pending;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.configuration.Keywords;
import org.jbehave.core.configuration.Keywords.StartingWordNotFound;
import org.jbehave.core.parsers.StepMatcher;
import org.jbehave.core.parsers.StepPatternParser;

import com.thoughtworks.paranamer.Paranamer;

/**
 * A StepCandidate is associated to a Java method annotated with {@link Given},
 * {@link When}, {@link Then} in a steps instance class. The StepCandidate is
 * responsible for matching the textual step against the pattern contained in
 * the method annotation via the {@link StepMatcher} and for the creation of the
 * matched executable step via the {@link StepCreator}.
 */
public class StepCandidate {

    private final String patternAsString;
    private final Integer priority;
    private final StepType stepType;
    private final Method method;
    private final Object stepsInstance;
    private final Keywords keywords;
    private final StepMatcher stepMatcher;
    private final StepCreator stepCreator;
    private StepMonitor stepMonitor = new SilentStepMonitor();
    private String[] composedSteps;

    public StepCandidate(String patternAsString, int priority, StepType stepType, Method method, Object stepsInstance,
            Keywords keywords, StepPatternParser stepPatternParser,
            ParameterConverters parameterConverters) {
        this.patternAsString = patternAsString;
        this.priority = priority;
        this.stepType = stepType;
        this.method = method;
        this.stepsInstance = stepsInstance;
        this.keywords = keywords;
        this.stepMatcher = stepPatternParser.parseStep(stepType, patternAsString);
        this.stepCreator = new StepCreator(stepsInstance, parameterConverters, stepMatcher, stepMonitor);
    }

    public Method getMethod() {
        return method;
    }

    public Integer getPriority() {
        return priority;
    }

    public String getPatternAsString() {
        return patternAsString;
    }

    public Object getStepsInstance() {
        return stepsInstance;
    }

    public StepType getStepType() {
        return stepType;
    }

    public String getStartingWord() {
        return keywords.startingWordFor(stepType);
    }

    public void useStepMonitor(StepMonitor stepMonitor) {
        this.stepMonitor = stepMonitor;
        this.stepCreator.useStepMonitor(stepMonitor);
    }

    public void doDryRun(boolean dryRun) {
        this.stepCreator.doDryRun(dryRun);
    }

    public void useParanamer(Paranamer paranamer) {
        this.stepCreator.useParanamer(paranamer);
    }

    public void composedOf(String[] steps) {
        this.composedSteps = steps;        
    }
    
    public boolean isComposite() {
        return composedSteps != null && composedSteps.length > 0;
    }

    public boolean ignore(String stepAsString) {
        try {
            String ignoreWord = keywords.startingWordFor(StepType.IGNORABLE);
            return keywords.stepStartsWithWord(stepAsString, ignoreWord);
        } catch (StartingWordNotFound e) {
            return false;
        }
    }
    
    public boolean isPending(){
        return method.isAnnotationPresent(Pending.class);
    }

    public boolean matches(String stepAsString) {
        return matches(stepAsString, null);
    }

    public boolean matches(String step, String previousNonAndStep) {
        try {
            boolean matchesType = true;
            if (isAndStep(step)) {
                if (previousNonAndStep == null) {
                    // cannot handle AND step with no previous step
                    matchesType = false;
                } else {
                    // previous step type should match candidate step type
                    matchesType = keywords.startingWordFor(stepType).equals(findStartingWord(previousNonAndStep));
                }
            }
            stepMonitor.stepMatchesType(step, previousNonAndStep, matchesType, stepType, method, stepsInstance);
            boolean matchesPattern = stepMatcher.matches(stripStartingWord(step));
            stepMonitor.stepMatchesPattern(step, matchesPattern, stepMatcher.pattern(), method, stepsInstance);
            // must match both type and pattern 
            return matchesType && matchesPattern;
        } catch (StartingWordNotFound e) {
            return false;
        }
    }

    public Step createMatchedStep(String stepAsString, Map<String, String> namedParameters) {
        return stepCreator.createParametrisedStep(method, stepAsString, stripStartingWord(stepAsString), namedParameters);
    }
    
    public List<Step> createComposedSteps(String stepAsString, Map<String, String> namedParameters,
            List<StepCandidate> allCandidates) {
        Map<String, String> matchedParameters;
        if ( namedParameters.isEmpty() ){
            matchedParameters = stepCreator.matchedParameters(method, stepAsString, stripStartingWord(stepAsString), namedParameters);
        } else {
            matchedParameters = namedParameters;
        }
        List<Step> steps = new ArrayList<Step>();
        for (String composedStep : composedSteps) {
            StepCandidate candidate = findComposedCandidate(composedStep, allCandidates);
            if ( candidate != null ){
                steps.add(candidate.createMatchedStep(composedStep, matchedParameters));
            }
        }
        return steps;
    }
<commit_msg>JBEHAVE-527:  Merged matched and named parameters when creating composed steps.<commit_after>package org.jbehave.core.steps;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Pending;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.configuration.Keywords;
import org.jbehave.core.configuration.Keywords.StartingWordNotFound;
import org.jbehave.core.parsers.StepMatcher;
import org.jbehave.core.parsers.StepPatternParser;

import com.thoughtworks.paranamer.Paranamer;

/**
 * A StepCandidate is associated to a Java method annotated with {@link Given},
 * {@link When}, {@link Then} in a steps instance class. The StepCandidate is
 * responsible for matching the textual step against the pattern contained in
 * the method annotation via the {@link StepMatcher} and for the creation of the
 * matched executable step via the {@link StepCreator}.
 */
public class StepCandidate {

    private final String patternAsString;
    private final Integer priority;
    private final StepType stepType;
    private final Method method;
    private final Object stepsInstance;
    private final Keywords keywords;
    private final StepMatcher stepMatcher;
    private final StepCreator stepCreator;
    private StepMonitor stepMonitor = new SilentStepMonitor();
    private String[] composedSteps;

    public StepCandidate(String patternAsString, int priority, StepType stepType, Method method, Object stepsInstance,
            Keywords keywords, StepPatternParser stepPatternParser,
            ParameterConverters parameterConverters) {
        this.patternAsString = patternAsString;
        this.priority = priority;
        this.stepType = stepType;
        this.method = method;
        this.stepsInstance = stepsInstance;
        this.keywords = keywords;
        this.stepMatcher = stepPatternParser.parseStep(stepType, patternAsString);
        this.stepCreator = new StepCreator(stepsInstance, parameterConverters, stepMatcher, stepMonitor);
    }

    public Method getMethod() {
        return method;
    }

    public Integer getPriority() {
        return priority;
    }

    public String getPatternAsString() {
        return patternAsString;
    }

    public Object getStepsInstance() {
        return stepsInstance;
    }

    public StepType getStepType() {
        return stepType;
    }

    public String getStartingWord() {
        return keywords.startingWordFor(stepType);
    }

    public void useStepMonitor(StepMonitor stepMonitor) {
        this.stepMonitor = stepMonitor;
        this.stepCreator.useStepMonitor(stepMonitor);
    }

    public void doDryRun(boolean dryRun) {
        this.stepCreator.doDryRun(dryRun);
    }

    public void useParanamer(Paranamer paranamer) {
        this.stepCreator.useParanamer(paranamer);
    }

    public void composedOf(String[] steps) {
        this.composedSteps = steps;        
    }
    
    public boolean isComposite() {
        return composedSteps != null && composedSteps.length > 0;
    }

    public boolean ignore(String stepAsString) {
        try {
            String ignoreWord = keywords.startingWordFor(StepType.IGNORABLE);
            return keywords.stepStartsWithWord(stepAsString, ignoreWord);
        } catch (StartingWordNotFound e) {
            return false;
        }
    }
    
    public boolean isPending(){
        return method.isAnnotationPresent(Pending.class);
    }

    public boolean matches(String stepAsString) {
        return matches(stepAsString, null);
    }

    public boolean matches(String step, String previousNonAndStep) {
        try {
            boolean matchesType = true;
            if (isAndStep(step)) {
                if (previousNonAndStep == null) {
                    // cannot handle AND step with no previous step
                    matchesType = false;
                } else {
                    // previous step type should match candidate step type
                    matchesType = keywords.startingWordFor(stepType).equals(findStartingWord(previousNonAndStep));
                }
            }
            stepMonitor.stepMatchesType(step, previousNonAndStep, matchesType, stepType, method, stepsInstance);
            boolean matchesPattern = stepMatcher.matches(stripStartingWord(step));
            stepMonitor.stepMatchesPattern(step, matchesPattern, stepMatcher.pattern(), method, stepsInstance);
            // must match both type and pattern 
            return matchesType && matchesPattern;
        } catch (StartingWordNotFound e) {
            return false;
        }
    }

    public Step createMatchedStep(String stepAsString, Map<String, String> namedParameters) {
        return stepCreator.createParametrisedStep(method, stepAsString, stripStartingWord(stepAsString), namedParameters);
    }
    
    public List<Step> createComposedSteps(String stepAsString, Map<String, String> namedParameters,
            List<StepCandidate> allCandidates) {
        Map<String, String> matchedParameters = stepCreator.matchedParameters(method, stepAsString, stripStartingWord(stepAsString), namedParameters);
        matchedParameters.putAll(namedParameters);
        List<Step> steps = new ArrayList<Step>();
        for (String composedStep : composedSteps) {
            StepCandidate candidate = findComposedCandidate(composedStep, allCandidates);
            if ( candidate != null ){
                steps.add(candidate.createMatchedStep(composedStep, matchedParameters));
            }
        }
        return steps;
    }

{'fraction_non_alphanumeric': 0.07713090465400035, 'fraction_numerical': 0.00043576782290395677, 'mean_word_length': 2.927104722792608, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3873112', 'n_tokens_mistral': 2982, 'n_tokens_neox': 2889, 'n_words': 750}