/* * 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 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 org.apache.commons.lang3.ObjectUtils; 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.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 java.util.ArrayList; import java.util.List; public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> { private final ExprModel mModel; private ParseTreeListener mParseTreeListener; 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); } } @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 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 mModel.group(ctx.children.get(1).accept(this)); } finally { onExit(ctx); } } @Override public Expr visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext 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 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 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; 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<Expr> args = new ArrayList<Expr>(); if (ctx.args != null) { for (ParseTree item : ctx.args.children) { if (ObjectUtils.equals(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<Expr> args = new ArrayList<Expr>(); if (ctx.resourceParameters() != null) { for (ParseTree item : ctx.resourceParameters().expressionList().children) { if (ObjectUtils.equals(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); } } }