All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.databinding.tool.ExpressionVisitor Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.databinding.tool;

import android.databinding.parser.BindingExpressionBaseVisitor;
import android.databinding.parser.BindingExpressionParser;
import android.databinding.parser.BindingExpressionParser.AndOrOpContext;
import android.databinding.parser.BindingExpressionParser.BinaryOpContext;
import android.databinding.parser.BindingExpressionParser.BitShiftOpContext;
import android.databinding.parser.BindingExpressionParser.InstanceOfOpContext;
import android.databinding.parser.BindingExpressionParser.UnaryOpContext;
import android.databinding.tool.expr.CallbackExprModel;
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
import android.databinding.tool.expr.StaticIdentifierExpr;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.util.Preconditions;

import com.google.common.base.Objects;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;

public class ExpressionVisitor extends BindingExpressionBaseVisitor {
    private ExprModel mModel;
    private ParseTreeListener mParseTreeListener;
    private ArrayDeque mModelStack = new ArrayDeque();

    public ExpressionVisitor(ExprModel model) {
        mModel = model;
    }

    public void setParseTreeListener(ParseTreeListener parseTreeListener) {
        mParseTreeListener = parseTreeListener;
    }

    private void onEnter(ParserRuleContext context) {
        if (mParseTreeListener != null) {
            mParseTreeListener.enterEveryRule(context);
        }
    }

    private void onExit(ParserRuleContext context) {
        if (mParseTreeListener != null) {
            mParseTreeListener.exitEveryRule(context);
        }
    }

    private void pushModel(ExprModel model) {
        Preconditions.checkNotNull(mModel, "Cannot put empty model to stack");
        Preconditions.checkNotNull(model, "Cannot set null model");
        mModelStack.push(mModel);
        mModel = model;
    }

    private void popModel() {
        Preconditions.checkNotNull(mModel, "Cannot have empty mdoel stack");
        Preconditions.check(mModelStack.size() > 0, "Cannot have empty model stack");
        mModel = mModelStack.pop();
    }

    @Override
    public Expr visitRootLambda(@NotNull BindingExpressionParser.RootLambdaContext ctx) {
        try {
            onEnter(ctx);
            CallbackExprModel callbackModel = new CallbackExprModel(mModel);
            ExprModel prev = mModel;
            pushModel(callbackModel);
            final BindingExpressionParser.LambdaExpressionContext lambdaCtx = ctx
                    .lambdaExpression();
            lambdaCtx.args.accept(this);
            return prev.lambdaExpr(lambdaCtx.expression().accept(this), callbackModel);
        } finally {
            popModel();
            onExit(ctx);
        }
    }

    @Override
    public Expr visitSingleLambdaParameter(
            @NotNull BindingExpressionParser.SingleLambdaParameterContext ctx) {
        try {
            onEnter(ctx);
            Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
                    + " callbacks.");
            // just add it to the callback model as identifier
            ((CallbackExprModel) mModel).callbackArg(ctx.getText());
            return null;
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitLambdaParameterList(
            @NotNull BindingExpressionParser.LambdaParameterListContext ctx) {
        try {
            onEnter(ctx);
            Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
                    + " callbacks.");
            if (ctx.params != null) {
                for (ParseTree item : ctx.params.children) {
                    if (Objects.equal(item.getText(), ",")) {
                        continue;
                    }
                    // just add them to the callback model as identifiers
                    ((CallbackExprModel) mModel).callbackArg(item.getText());
                }
            }
            return null;
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) {
        try {
            onEnter(ctx);
            final String javaString;
            if (ctx.SingleQuoteString() != null) {
                String str = ctx.SingleQuoteString().getText();
                String contents = str.substring(1, str.length() - 1);
                contents = contents.replace("\"", "\\\"").replace("\\`", "`");
                javaString = '"' + contents + '"';
            } else {
                javaString = ctx.DoubleQuoteString().getText();
            }
            return mModel.symbol(javaString, String.class);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitRootExpr(@NotNull BindingExpressionParser.RootExprContext ctx) {
        try {
            onEnter(ctx);
            // TODO handle defaults
            return mModel.bindingExpr(ctx.expression().accept(this));
        } catch (Exception e) {
            System.out.println("Error while parsing! " + ctx.getText());
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) {
        try {
            onEnter(ctx);
            Preconditions.check(ctx.children.size() == 3, "Grouping expression should have"
                    + " 3 children. # of children: %d", ctx.children.size());
            return ctx.children.get(1).accept(this);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) {
        try {
            onEnter(ctx);
            ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
            ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports());
            if (modelClass == null) {
                return mModel.field(ctx.expression().accept(this),
                        ctx.Identifier().getSymbol().getText());
            } else {
                String name = modelClass.toJavaCode();
                StaticIdentifierExpr expr = mModel.staticIdentifier(name);
                expr.setUserDefinedType(name);
                return expr;
            }
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitFunctionRef(@NotNull BindingExpressionParser.FunctionRefContext ctx) {
        try {
            onEnter(ctx);
            return mModel.methodReference(ctx.expression().accept(this),
                    ctx.Identifier().getSymbol().getText());
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
        try {
            onEnter(ctx);
            final Expr left = ctx.left.accept(this);
            return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
                    ctx.right.accept(this), left);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitTerminal(@NotNull TerminalNode node) {
        try {
            onEnter((ParserRuleContext) node.getParent().getRuleContext());
            final int type = node.getSymbol().getType();
            Class classType;
            switch (type) {
                case BindingExpressionParser.IntegerLiteral:
                    classType = int.class;
                    break;
                case BindingExpressionParser.FloatingPointLiteral:
                    classType = float.class;
                    break;
                case BindingExpressionParser.BooleanLiteral:
                    classType = boolean.class;
                    break;
                case BindingExpressionParser.CharacterLiteral:
                    classType = char.class;
                    break;
                case BindingExpressionParser.SingleQuoteString:
                case BindingExpressionParser.DoubleQuoteString:
                    classType = String.class;
                    break;
                case BindingExpressionParser.NullLiteral:
                    classType = Object.class;
                    break;
                case BindingExpressionParser.VoidLiteral:
                    classType = void.class;
                    break;
                default:
                    throw new RuntimeException("cannot create expression from terminal node " +
                            node.toString());
            }
            return mModel.symbol(node.getText(), classType);
        } finally {
            onExit((ParserRuleContext) node.getParent().getRuleContext());
        }
    }

    @Override
    public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) {
        try {
            onEnter(ctx);
            return mModel.identifier(ctx.getText());
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this),
                    ctx.iffalse.accept(this));
        } finally {
            onExit(ctx);
        }

    }

    @Override
    public Expr visitMethodInvocation(
            @NotNull BindingExpressionParser.MethodInvocationContext ctx) {
        try {
            onEnter(ctx);
            List args = new ArrayList();
            if (ctx.args != null) {
                for (ParseTree item : ctx.args.children) {
                    if (Objects.equal(item.getText(), ",")) {
                        continue;
                    }
                    args.add(item.accept(this));
                }
            }
            return mModel.methodCall(ctx.target.accept(this),
                    ctx.Identifier().getText(), args);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitAndOrOp(@NotNull AndOrOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.logical(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitBinaryOp(@NotNull BinaryOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitBitShiftOp(@NotNull BitShiftOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.bitshift(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitInstanceOfOp(@NotNull InstanceOfOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.instanceOfOp(ctx.expression().accept(this), ctx.type().getText());
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitUnaryOp(@NotNull UnaryOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.unary(ctx.op.getText(), ctx.expression().accept(this));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) {
        try {
            onEnter(ctx);
            final List args = new ArrayList();
            if (ctx.resourceParameters() != null) {
                for (ParseTree item : ctx.resourceParameters().expressionList().children) {
                    if (Objects.equal(item.getText(), ",")) {
                        continue;
                    }
                    args.add(item.accept(this));
                }
            }
            final String resourceReference = ctx.ResourceReference().getText();
            final int colonIndex = resourceReference.indexOf(':');
            final int slashIndex = resourceReference.indexOf('/');
            final String packageName = colonIndex < 0 ? null :
                    resourceReference.substring(1, colonIndex).trim();
            final int startIndex = Math.max(1, colonIndex + 1);
            final String resourceType = resourceReference.substring(startIndex, slashIndex).trim();
            final String resourceName = resourceReference.substring(slashIndex + 1).trim();
            return mModel.resourceExpr(packageName, resourceType, resourceName, args);
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1)));
        } finally {
            onExit(ctx);
        }
    }

    @Override
    public Expr visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) {
        try {
            onEnter(ctx);
            return mModel.castExpr(ctx.type().getText(), visit(ctx.expression()));
        } finally {
            onExit(ctx);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy