mongofx.js.support.JsAntlrPathBuilder.java Source code

Java tutorial

Introduction

Here is the source code for mongofx.js.support.JsAntlrPathBuilder.java

Source

// This file is part of MongoFX.
//
// MongoFX is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
// MongoFX is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MongoFX.  If not, see <http://www.gnu.org/licenses/>.

//
// Copyright (c) Andrey Dubravin, 2015
//
package mongofx.js.support;

import java.util.*;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;

import mongofx.js.antlr4.parser.ECMAScriptBaseVisitor;
import mongofx.js.antlr4.parser.ECMAScriptLexer;
import mongofx.js.antlr4.parser.ECMAScriptParser;
import org.antlr.v4.runtime.tree.TerminalNode;

public class JsAntlrPathBuilder {
    public static Optional<List<String>> buildPath(String jsCode, int position) {
        if (position <= 0) {
            return Optional.of(Collections.singletonList(""));
        }
        position--;

        ECMAScriptLexer lexer = new ECMAScriptLexer(new ANTLRInputStream(jsCode));
        ECMAScriptParser parser = new ECMAScriptParser(new CommonTokenStream(lexer));

        ScriptVisitor scriptVisitor = new ScriptVisitor(position);
        scriptVisitor.visit(parser.program());
        return scriptVisitor.getPath();
    }

    private static class ScriptVisitor extends ECMAScriptBaseVisitor<Void> {
        private Stack<DotPath> stack = new Stack<>();

        private final int position;
        boolean foundPath;

        public ScriptVisitor(int position) {
            this.position = position;
            foundPath = false;
        }

        public Optional<List<String>> getPath() {
            if (foundPath) {
                return Optional.of(stack.lastElement().path);
            }
            return Optional.empty();
        }

        @Override
        public Void visitMemberDotExpression(ECMAScriptParser.MemberDotExpressionContext ctx) {
            if (stack.isEmpty()) {
                stack.add(new DotPath());
            }
            stack.lastElement().levelDown();

            super.visitMemberDotExpression(ctx);

            DotPath dotPath = stack.lastElement();
            dotPath.levelUp();
            if (dotPath.isTopLevel() && !foundPath) {
                stack.pop();
            }

            return null;
        }

        @Override
        public Void visitIdentifierName(ECMAScriptParser.IdentifierNameContext ctx) {
            if (foundPath || stack.isEmpty()) {
                return null;
            }
            markFound(ctx.getStart());
            addPath(ctx);

            return super.visitIdentifierName(ctx);
        }

        @Override
        public Void visitArguments(ECMAScriptParser.ArgumentsContext ctx) {
            // function context must be processed here
            if (!stack.isEmpty()) {
                return null;
            }
            return super.visitArguments(ctx);
        }

        @Override
        public Void visitIdentifierExpression(ECMAScriptParser.IdentifierExpressionContext ctx) {
            if (foundPath) {
                return null;
            }

            if (markFound(ctx.getStart())) {
                if (stack.isEmpty()) {
                    stack.add(new DotPath());
                }
            }

            if (stack.isEmpty()) {
                return null;
            }

            addPath(ctx);
            return super.visitIdentifierExpression(ctx);
        }

        @Override
        public Void visitTerminal(TerminalNode node) {
            if (foundPath || stack.isEmpty()) {
                return null;
            }

            if (".".equals(node.getText())) {
                if (markFound(node.getSymbol())) {
                    stack.lastElement().addToPath("");
                    return null;
                }
            }
            return super.visitTerminal(node);
        }

        private void addPath(ParserRuleContext ctx) {
            if (ctx.exception == null) {
                if (foundPath) {
                    Token start = ctx.getStart();
                    int partLength = position + 1 - Math.min(start.getStartIndex(), start.getStopIndex());
                    if (partLength > 0) {
                        stack.lastElement().addToPath(ctx.getText().substring(0, partLength));
                    } else {
                        stack.lastElement().addToPath("");
                    }
                } else {
                    stack.lastElement().addToPath(ctx.getText());
                }
            } else {
                stack.lastElement().addToPath(ctx.getText());
            }
        }

        private boolean markFound(Token token) {
            int start = Math.min(token.getStartIndex(), token.getStopIndex());
            int stop = Math.max(token.getStartIndex(), token.getStopIndex());
            if (position >= start && position <= stop) {
                foundPath = true;
            }
            return foundPath;
        }
    }

    private static class DotPath {
        private final List<String> path = new ArrayList<>(3);
        int level = 0;

        public void levelDown() {
            level++;
        }

        public void levelUp() {
            level--;
        }

        public boolean isTopLevel() {
            return level <= 0;
        }

        public void addToPath(String value) {
            path.add(value);
        }
    }
}