Home | History | Annotate | Download | only in tool
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.databinding.tool;
     18 
     19 import android.databinding.parser.BindingExpressionBaseVisitor;
     20 import android.databinding.parser.BindingExpressionParser;
     21 import android.databinding.parser.BindingExpressionParser.AndOrOpContext;
     22 import android.databinding.parser.BindingExpressionParser.BinaryOpContext;
     23 import android.databinding.parser.BindingExpressionParser.BitShiftOpContext;
     24 import android.databinding.parser.BindingExpressionParser.InstanceOfOpContext;
     25 import android.databinding.parser.BindingExpressionParser.UnaryOpContext;
     26 import android.databinding.tool.expr.CallbackExprModel;
     27 import android.databinding.tool.expr.Expr;
     28 import android.databinding.tool.expr.ExprModel;
     29 import android.databinding.tool.expr.StaticIdentifierExpr;
     30 import android.databinding.tool.reflection.ModelAnalyzer;
     31 import android.databinding.tool.reflection.ModelClass;
     32 import android.databinding.tool.util.Preconditions;
     33 
     34 import com.android.annotations.NonNull;
     35 import com.google.common.base.Objects;
     36 
     37 import org.antlr.v4.runtime.ParserRuleContext;
     38 import org.antlr.v4.runtime.tree.ParseTree;
     39 import org.antlr.v4.runtime.tree.ParseTreeListener;
     40 import org.antlr.v4.runtime.tree.TerminalNode;
     41 
     42 import java.util.ArrayDeque;
     43 import java.util.ArrayList;
     44 import java.util.List;
     45 
     46 class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
     47     private ExprModel mModel;
     48     private ParseTreeListener mParseTreeListener;
     49     private ArrayDeque<ExprModel> mModelStack = new ArrayDeque<ExprModel>();
     50     private BindingTarget mTarget;
     51 
     52     ExpressionVisitor(ExprModel model) {
     53         mModel = model;
     54     }
     55 
     56     void setParseTreeListener(ParseTreeListener parseTreeListener) {
     57         mParseTreeListener = parseTreeListener;
     58     }
     59 
     60     public void setBindingTarget(BindingTarget bindingTarget) {
     61         mTarget = bindingTarget;
     62     }
     63 
     64     private void onEnter(ParserRuleContext context) {
     65         if (mParseTreeListener != null) {
     66             mParseTreeListener.enterEveryRule(context);
     67         }
     68     }
     69 
     70     private void onExit(ParserRuleContext context) {
     71         if (mParseTreeListener != null) {
     72             mParseTreeListener.exitEveryRule(context);
     73         }
     74     }
     75 
     76     private void pushModel(ExprModel model) {
     77         Preconditions.checkNotNull(mModel, "Cannot put empty model to stack");
     78         Preconditions.checkNotNull(model, "Cannot set null model");
     79         mModelStack.push(mModel);
     80         mModel = model;
     81     }
     82 
     83     private void popModel() {
     84         Preconditions.checkNotNull(mModel, "Cannot have empty mdoel stack");
     85         Preconditions.check(mModelStack.size() > 0, "Cannot have empty model stack");
     86         mModel = mModelStack.pop();
     87     }
     88 
     89     @Override
     90     public Expr visitRootLambda(@NonNull BindingExpressionParser.RootLambdaContext ctx) {
     91         try {
     92             onEnter(ctx);
     93             CallbackExprModel callbackModel = new CallbackExprModel(mModel);
     94             ExprModel prev = mModel;
     95             pushModel(callbackModel);
     96             final BindingExpressionParser.LambdaExpressionContext lambdaCtx = ctx
     97                     .lambdaExpression();
     98             lambdaCtx.args.accept(this);
     99             return prev.lambdaExpr(lambdaCtx.expression().accept(this), callbackModel);
    100         } finally {
    101             popModel();
    102             onExit(ctx);
    103         }
    104     }
    105 
    106     @Override
    107     public Expr visitSingleLambdaParameter(
    108             @NonNull BindingExpressionParser.SingleLambdaParameterContext ctx) {
    109         try {
    110             onEnter(ctx);
    111             Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
    112                     + " callbacks.");
    113             // just add it to the callback model as identifier
    114             ((CallbackExprModel) mModel).callbackArg(ctx.getText());
    115             return null;
    116         } finally {
    117             onExit(ctx);
    118         }
    119     }
    120 
    121     @Override
    122     public Expr visitLambdaParameterList(
    123             @NonNull BindingExpressionParser.LambdaParameterListContext ctx) {
    124         try {
    125             onEnter(ctx);
    126             Preconditions.check(mModel instanceof CallbackExprModel, "Lambdas can only be used in"
    127                     + " callbacks.");
    128             if (ctx.params != null) {
    129                 for (ParseTree item : ctx.params.children) {
    130                     if (Objects.equal(item.getText(), ",")) {
    131                         continue;
    132                     }
    133                     // just add them to the callback model as identifiers
    134                     ((CallbackExprModel) mModel).callbackArg(item.getText());
    135                 }
    136             }
    137             return null;
    138         } finally {
    139             onExit(ctx);
    140         }
    141     }
    142 
    143     @Override
    144     public Expr visitStringLiteral(@NonNull BindingExpressionParser.StringLiteralContext ctx) {
    145         try {
    146             onEnter(ctx);
    147             final String javaString;
    148             if (ctx.SingleQuoteString() != null) {
    149                 String str = ctx.SingleQuoteString().getText();
    150                 String contents = str.substring(1, str.length() - 1);
    151                 contents = contents.replace("\"", "\\\"").replace("\\`", "`");
    152                 javaString = '"' + contents + '"';
    153             } else {
    154                 javaString = ctx.DoubleQuoteString().getText();
    155             }
    156             return mModel.symbol(javaString, String.class);
    157         } finally {
    158             onExit(ctx);
    159         }
    160     }
    161 
    162     @Override
    163     public Expr visitRootExpr(@NonNull BindingExpressionParser.RootExprContext ctx) {
    164         try {
    165             onEnter(ctx);
    166             // TODO handle defaults
    167             return mModel.bindingExpr(ctx.expression().accept(this));
    168         } catch (Exception e) {
    169             System.out.println("Error while parsing! " + ctx.getText());
    170             e.printStackTrace();
    171             throw new RuntimeException(e);
    172         } finally {
    173             onExit(ctx);
    174         }
    175     }
    176 
    177     @Override
    178     public Expr visitGrouping(@NonNull BindingExpressionParser.GroupingContext ctx) {
    179         try {
    180             onEnter(ctx);
    181             Preconditions.check(ctx.children.size() == 3, "Grouping expression should have"
    182                     + " 3 children. # of children: %d", ctx.children.size());
    183             return ctx.children.get(1).accept(this);
    184         } finally {
    185             onExit(ctx);
    186         }
    187     }
    188 
    189     @Override
    190     public Expr visitDotOp(@NonNull BindingExpressionParser.DotOpContext ctx) {
    191         try {
    192             onEnter(ctx);
    193             ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
    194             ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports());
    195             if (modelClass == null) {
    196                 return mModel.field(ctx.expression().accept(this),
    197                         ctx.Identifier().getSymbol().getText());
    198             } else {
    199                 String name = modelClass.toJavaCode();
    200                 StaticIdentifierExpr expr = mModel.staticIdentifier(name);
    201                 expr.setUserDefinedType(name);
    202                 return expr;
    203             }
    204         } finally {
    205             onExit(ctx);
    206         }
    207     }
    208 
    209     @Override
    210     public Expr visitFunctionRef(@NonNull BindingExpressionParser.FunctionRefContext ctx) {
    211         try {
    212             onEnter(ctx);
    213             return mModel.methodReference(ctx.expression().accept(this),
    214                     ctx.Identifier().getSymbol().getText());
    215         } finally {
    216             onExit(ctx);
    217         }
    218     }
    219 
    220     @Override
    221     public Expr visitQuestionQuestionOp(
    222             @NonNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
    223         try {
    224             onEnter(ctx);
    225             final Expr left = ctx.left.accept(this);
    226             return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
    227                     ctx.right.accept(this), left);
    228         } finally {
    229             onExit(ctx);
    230         }
    231     }
    232 
    233     @Override
    234     public Expr visitTerminal(@NonNull TerminalNode node) {
    235         try {
    236             onEnter((ParserRuleContext) node.getParent());
    237             final int type = node.getSymbol().getType();
    238             Class classType;
    239             switch (type) {
    240                 case BindingExpressionParser.IntegerLiteral:
    241                     classType = int.class;
    242                     break;
    243                 case BindingExpressionParser.FloatingPointLiteral:
    244                     classType = float.class;
    245                     break;
    246                 case BindingExpressionParser.BooleanLiteral:
    247                     classType = boolean.class;
    248                     break;
    249                 case BindingExpressionParser.CharacterLiteral:
    250                     classType = char.class;
    251                     break;
    252                 case BindingExpressionParser.SingleQuoteString:
    253                 case BindingExpressionParser.DoubleQuoteString:
    254                     classType = String.class;
    255                     break;
    256                 case BindingExpressionParser.NullLiteral:
    257                     classType = Object.class;
    258                     break;
    259                 case BindingExpressionParser.VoidLiteral:
    260                     classType = void.class;
    261                     break;
    262                 default:
    263                     throw new RuntimeException("cannot create expression from terminal node " +
    264                             node.toString());
    265             }
    266             return mModel.symbol(node.getText(), classType);
    267         } finally {
    268             onExit((ParserRuleContext) node.getParent());
    269         }
    270     }
    271 
    272     @Override
    273     public Expr visitComparisonOp(@NonNull BindingExpressionParser.ComparisonOpContext ctx) {
    274         try {
    275             onEnter(ctx);
    276             return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this));
    277         } finally {
    278             onExit(ctx);
    279         }
    280     }
    281 
    282     @Override
    283     public Expr visitIdentifier(@NonNull BindingExpressionParser.IdentifierContext ctx) {
    284         try {
    285             onEnter(ctx);
    286             return mModel.identifier(ctx.getText());
    287         } finally {
    288             onExit(ctx);
    289         }
    290     }
    291 
    292     @Override
    293     public Expr visitTernaryOp(@NonNull BindingExpressionParser.TernaryOpContext ctx) {
    294         try {
    295             onEnter(ctx);
    296             return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this),
    297                     ctx.iffalse.accept(this));
    298         } finally {
    299             onExit(ctx);
    300         }
    301 
    302     }
    303 
    304     @Override
    305     public Expr visitMethodInvocation(
    306             @NonNull BindingExpressionParser.MethodInvocationContext ctx) {
    307         try {
    308             onEnter(ctx);
    309             List<Expr> args = new ArrayList<Expr>();
    310             if (ctx.args != null) {
    311                 for (ParseTree item : ctx.args.children) {
    312                     if (Objects.equal(item.getText(), ",")) {
    313                         continue;
    314                     }
    315                     args.add(item.accept(this));
    316                 }
    317             }
    318             return mModel.methodCall(ctx.target.accept(this),
    319                     ctx.Identifier().getText(), args);
    320         } finally {
    321             onExit(ctx);
    322         }
    323     }
    324 
    325     @Override
    326     public Expr visitMathOp(@NonNull BindingExpressionParser.MathOpContext ctx) {
    327         try {
    328             onEnter(ctx);
    329             return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    330         } finally {
    331             onExit(ctx);
    332         }
    333     }
    334 
    335     @Override
    336     public Expr visitAndOrOp(@NonNull AndOrOpContext ctx) {
    337         try {
    338             onEnter(ctx);
    339             return mModel.logical(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    340         } finally {
    341             onExit(ctx);
    342         }
    343     }
    344 
    345     @Override
    346     public Expr visitBinaryOp(@NonNull BinaryOpContext ctx) {
    347         try {
    348             onEnter(ctx);
    349             return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    350         } finally {
    351             onExit(ctx);
    352         }
    353     }
    354 
    355     @Override
    356     public Expr visitBitShiftOp(@NonNull BitShiftOpContext ctx) {
    357         try {
    358             onEnter(ctx);
    359             return mModel.bitshift(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    360         } finally {
    361             onExit(ctx);
    362         }
    363     }
    364 
    365     @Override
    366     public Expr visitInstanceOfOp(@NonNull InstanceOfOpContext ctx) {
    367         try {
    368             onEnter(ctx);
    369             return mModel.instanceOfOp(ctx.expression().accept(this), ctx.type().getText());
    370         } finally {
    371             onExit(ctx);
    372         }
    373     }
    374 
    375     @Override
    376     public Expr visitUnaryOp(@NonNull UnaryOpContext ctx) {
    377         try {
    378             onEnter(ctx);
    379             return mModel.unary(ctx.op.getText(), ctx.expression().accept(this));
    380         } finally {
    381             onExit(ctx);
    382         }
    383     }
    384 
    385     @Override
    386     public Expr visitResources(@NonNull BindingExpressionParser.ResourcesContext ctx) {
    387         try {
    388             onEnter(ctx);
    389             final List<Expr> args = new ArrayList<Expr>();
    390             if (ctx.resourceParameters() != null) {
    391                 for (ParseTree item : ctx.resourceParameters().expressionList().children) {
    392                     if (Objects.equal(item.getText(), ",")) {
    393                         continue;
    394                     }
    395                     args.add(item.accept(this));
    396                 }
    397             }
    398             final String resourceReference = ctx.ResourceReference().getText();
    399             final int colonIndex = resourceReference.indexOf(':');
    400             final int slashIndex = resourceReference.indexOf('/');
    401             final String packageName = colonIndex < 0 ? null :
    402                     resourceReference.substring(1, colonIndex).trim();
    403             final int startIndex = Math.max(1, colonIndex + 1);
    404             final String resourceType = resourceReference.substring(startIndex, slashIndex).trim();
    405             final String resourceName = resourceReference.substring(slashIndex + 1).trim();
    406             return mModel.resourceExpr(mTarget, packageName, resourceType, resourceName, args);
    407         } finally {
    408             onExit(ctx);
    409         }
    410     }
    411 
    412     @Override
    413     public Expr visitBracketOp(@NonNull BindingExpressionParser.BracketOpContext ctx) {
    414         try {
    415             onEnter(ctx);
    416             return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1)));
    417         } finally {
    418             onExit(ctx);
    419         }
    420     }
    421 
    422     @Override
    423     public Expr visitCastOp(@NonNull BindingExpressionParser.CastOpContext ctx) {
    424         try {
    425             onEnter(ctx);
    426             return mModel.castExpr(ctx.type().getText(), visit(ctx.expression()));
    427         } finally {
    428             onExit(ctx);
    429         }
    430     }
    431 }
    432