/*
* 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);
}
}
}