Package gherkin.parser

Source Code of gherkin.parser.Parser$Machine

package gherkin.parser;

import gherkin.I18n;
import gherkin.formatter.Formatter;
import gherkin.lexer.I18nLexer;
import gherkin.lexer.Listener;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Parser implements Listener {
    List<Machine> machines = new ArrayList<Machine>();

    private final boolean throwOnError;
    private final String machineName;
    private FormatterListener listener;
    private I18nLexer lexer;
    private String featureURI;
    private Integer lineOffset;
    private final Formatter formatter;

    public Parser(Formatter formatter) {
        this(formatter, true);
    }

    public Parser(Formatter formatter, boolean throwOnError) {
        this(formatter, throwOnError, "root");
    }

    public Parser(Formatter formatter, boolean throwOnError, String machineName) {
        this(formatter, throwOnError, machineName, false);
    }

    public Parser(Formatter formatter, boolean throwOnError, String machineName, boolean forceRubyDummy) {
        this(formatter, throwOnError, machineName, forceRubyDummy, "en");
    }

    public Parser(Formatter formatter, boolean throwOnError, String machineName, boolean forceRubyDummy, String isoCode) {
        if (formatter == null) throw new NullPointerException("formatter");
        this.formatter = formatter;
        this.listener = new FormatterListener(formatter);
        this.throwOnError = throwOnError;
        this.machineName = machineName;
        this.lexer = new I18nLexer(this, forceRubyDummy, isoCode);
    }

    /**
     * @param featureURI the URI where the gherkin originated from. Typically a file path.
     * @param lineOffset the line offset within the uri document the gherkin was taken from. Typically 0.
     */
    public void parse(String gherkin, String featureURI, Integer lineOffset) {
        formatter.uri(featureURI);
        this.featureURI = featureURI;
        this.lineOffset = lineOffset;
        pushMachine(machineName);
        try {
            lexer.scan(gherkin);
        } finally {
            popMachine();
        }
    }

    public I18n getI18nLanguage() {
        return lexer.getI18nLanguage();
    }

    private void pushMachine(String machineName) {
        machines.add(new Machine(this, machineName, featureURI));
    }

    private void popMachine() {
        machines.remove(machines.size() - 1);
    }

    @Override
    public void tag(String tag, Integer line) {
        if (event("tag", line)) {
            listener.tag(tag, line);
        }
    }

    @Override
    public void docString(String contentType, String content, Integer line) {
        if (event("doc_string", line)) {
            listener.docString(contentType, content, line);
        }
    }

    @Override
    public void feature(String keyword, String name, String description, Integer line) {
        if (event("feature", line)) {
            listener.feature(keyword, name, description, line);
        }
    }

    @Override
    public void background(String keyword, String name, String description, Integer line) {
        if (event("background", line)) {
            listener.background(keyword, name, description, line);
        }
    }

    @Override
    public void scenario(String keyword, String name, String description, Integer line) {
        if (event("scenario", line)) {
            listener.scenario(keyword, name, description, line);
        }
    }

    @Override
    public void scenarioOutline(String keyword, String name, String description, Integer line) {
        if (event("scenario_outline", line)) {
            listener.scenarioOutline(keyword, name, description, line);
        }
    }

    @Override
    public void examples(String keyword, String name, String description, Integer line) {
        if (event("examples", line)) {
            listener.examples(keyword, name, description, line);
        }
    }

    @Override
    public void step(String keyword, String name, Integer line) {
        if (event("step", line)) {
            listener.step(keyword, name, line);
        }
    }

    @Override
    public void comment(String comment, Integer line) {
        if (event("comment", line)) {
            listener.comment(comment, line);
        }
    }

    @Override
    public void row(List<String> cells, Integer line) {
        if (event("row", line)) {
            listener.row(cells, line);
        }
    }

    @Override
    public void eof() {
        if (event("eof", 1)) {
            listener.eof();
        }
    }

    private boolean event(String event, Integer line) {
        try {
            machine().event(event, line);
            return true;
        } catch (ParseError e) {
            if (throwOnError) {
                throw e;
            } else {
                int l = lineOffset + line;
                listener.syntaxError(e.getState(), event, e.getLegalEvents(), featureURI, l);
                return false;
            }
        }
    }

    private Machine machine() {
        return machines.get(machines.size() - 1);
    }

    private static class Machine {
        private static final Pattern PUSH = Pattern.compile("push\\((.+)\\)");
        private static final Map<String, Map<String, Map<String, String>>> TRANSITION_MAPS = new HashMap<String, Map<String, Map<String, String>>>();

        private final Parser parser;
        private final String name;
        private final String uri;
        private String state;
        private Map<String, Map<String, String>> transitionMap;

        public Machine(Parser parser, String name, String uri) {
            if (uri == null) {
                throw new NullPointerException("uri");
            }
            this.parser = parser;
            this.name = name;
            this.state = name;
            this.uri = uri;
            this.transitionMap = transitionMap(name);
        }

        public void event(String event, Integer line) {
            Map<String, String> states = transitionMap.get(state);
            if (states == null) {
                throw new RuntimeException("Unknown getState: " + state + " for machine " + name);
            }
            String newState = states.get(event);
            if (newState == null) {
                throw new RuntimeException("Unknown transition: " + event + " among " + states + " for machine " + name + " in getState " + state);
            }
            if ("E".equals(newState)) {
                throw new ParseError(state, event, expectedEvents(), uri, line);
            } else {
                Matcher push = PUSH.matcher(newState);
                if (push.matches()) {
                    parser.pushMachine(push.group(1));
                    parser.event(event, line);
                } else if ("pop()".equals(newState)) {
                    parser.popMachine();
                    parser.event(event, line);
                } else {
                    state = newState;
                }
            }
        }

        private List<String> expectedEvents() {
            List<String> result = new ArrayList<String>();
            for (String event : transitionMap.get(state).keySet()) {
                if (!transitionMap.get(state).get(event).equals("E")) {
                    result.add(event);
                }
            }
            Collections.sort(result);
            result.remove("eof");
            return result;
        }

        private Map<String, Map<String, String>> transitionMap(String name) {
            Map<String, Map<String, String>> map = TRANSITION_MAPS.get(name);
            if (map == null) {
                map = buildTransitionMap(name);
                TRANSITION_MAPS.put(name, map);
            }
            return map;
        }

        private Map<String, Map<String, String>> buildTransitionMap(String name) {
            Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
            List<List<String>> transitionTable = new StateMachineReader(name).transitionTable();
            List<String> events = transitionTable.get(0).subList(1, transitionTable.get(0).size());
            for (List<String> actions : transitionTable.subList(1, transitionTable.size())) {
                Map<String, String> transitions = new HashMap<String, String>();
                int col = 1;
                for (String event : events) {
                    transitions.put(event, actions.get(col++));
                }
                String state = actions.get(0);
                result.put(state, transitions);
            }
            return result;
        }
    }
}
TOP

Related Classes of gherkin.parser.Parser$Machine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.