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 com.google.common.base.Objects;
     20 
     21 import org.antlr.v4.runtime.ParserRuleContext;
     22 import org.antlr.v4.runtime.misc.NotNull;
     23 import org.antlr.v4.runtime.tree.ParseTree;
     24 import org.antlr.v4.runtime.tree.ParseTreeListener;
     25 import org.antlr.v4.runtime.tree.TerminalNode;
     26 
     27 import android.databinding.parser.BindingExpressionBaseVisitor;
     28 import android.databinding.parser.BindingExpressionParser;
     29 import android.databinding.parser.BindingExpressionParser.AndOrOpContext;
     30 import android.databinding.parser.BindingExpressionParser.BinaryOpContext;
     31 import android.databinding.parser.BindingExpressionParser.BitShiftOpContext;
     32 import android.databinding.parser.BindingExpressionParser.InstanceOfOpContext;
     33 import android.databinding.parser.BindingExpressionParser.UnaryOpContext;
     34 import android.databinding.tool.expr.Expr;
     35 import android.databinding.tool.expr.ExprModel;
     36 import android.databinding.tool.expr.StaticIdentifierExpr;
     37 import android.databinding.tool.reflection.ModelAnalyzer;
     38 import android.databinding.tool.reflection.ModelClass;
     39 import android.databinding.tool.util.Preconditions;
     40 
     41 import java.util.ArrayList;
     42 import java.util.List;
     43 
     44 public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
     45     private final ExprModel mModel;
     46     private ParseTreeListener mParseTreeListener;
     47 
     48     public ExpressionVisitor(ExprModel model) {
     49         mModel = model;
     50     }
     51 
     52     public void setParseTreeListener(ParseTreeListener parseTreeListener) {
     53         mParseTreeListener = parseTreeListener;
     54     }
     55 
     56     private void onEnter(ParserRuleContext context) {
     57         if (mParseTreeListener != null) {
     58             mParseTreeListener.enterEveryRule(context);
     59         }
     60     }
     61 
     62     private void onExit(ParserRuleContext context) {
     63         if (mParseTreeListener != null) {
     64             mParseTreeListener.exitEveryRule(context);
     65         }
     66     }
     67 
     68     @Override
     69     public Expr visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) {
     70         try {
     71             onEnter(ctx);
     72             final String javaString;
     73             if (ctx.SingleQuoteString() != null) {
     74                 String str = ctx.SingleQuoteString().getText();
     75                 String contents = str.substring(1, str.length() - 1);
     76                 contents = contents.replace("\"", "\\\"").replace("\\`", "`");
     77                 javaString = '"' + contents + '"';
     78             } else {
     79                 javaString = ctx.DoubleQuoteString().getText();
     80             }
     81             return mModel.symbol(javaString, String.class);
     82         } finally {
     83             onExit(ctx);
     84         }
     85     }
     86 
     87     @Override
     88     public Expr visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) {
     89         try {
     90             onEnter(ctx);
     91             Preconditions.check(ctx.children.size() == 3, "Grouping expression should have"
     92                     + " 3 children. # of children: %d", ctx.children.size());
     93             return mModel.group(ctx.children.get(1).accept(this));
     94         } finally {
     95             onExit(ctx);
     96         }
     97     }
     98 
     99     @Override
    100     public Expr visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) {
    101         try {
    102             onEnter(ctx);
    103             // TODO handle defaults
    104             return mModel.bindingExpr(ctx.expression().accept(this));
    105         } catch (Exception e) {
    106             System.out.println("Error while parsing! " + ctx.getText());
    107             e.printStackTrace();
    108             throw new RuntimeException(e);
    109         } finally {
    110             onExit(ctx);
    111         }
    112     }
    113 
    114     @Override
    115     public Expr visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) {
    116         try {
    117             onEnter(ctx);
    118             ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
    119             ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports());
    120             if (modelClass == null) {
    121                 return mModel.field(ctx.expression().accept(this),
    122                         ctx.Identifier().getSymbol().getText());
    123             } else {
    124                 String name = modelClass.toJavaCode();
    125                 StaticIdentifierExpr expr = mModel.staticIdentifier(name);
    126                 expr.setUserDefinedType(name);
    127                 return expr;
    128             }
    129         } finally {
    130             onExit(ctx);
    131         }
    132     }
    133 
    134     @Override
    135     public Expr visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
    136         try {
    137             onEnter(ctx);
    138             final Expr left = ctx.left.accept(this);
    139             return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
    140                     ctx.right.accept(this), left);
    141         } finally {
    142             onExit(ctx);
    143         }
    144     }
    145 
    146     @Override
    147     public Expr visitTerminal(@NotNull TerminalNode node) {
    148         try {
    149             onEnter((ParserRuleContext) node.getParent().getRuleContext());
    150             final int type = node.getSymbol().getType();
    151             Class classType;
    152             switch (type) {
    153                 case BindingExpressionParser.IntegerLiteral:
    154                     classType = int.class;
    155                     break;
    156                 case BindingExpressionParser.FloatingPointLiteral:
    157                     classType = float.class;
    158                     break;
    159                 case BindingExpressionParser.BooleanLiteral:
    160                     classType = boolean.class;
    161                     break;
    162                 case BindingExpressionParser.CharacterLiteral:
    163                     classType = char.class;
    164                     break;
    165                 case BindingExpressionParser.SingleQuoteString:
    166                 case BindingExpressionParser.DoubleQuoteString:
    167                     classType = String.class;
    168                     break;
    169                 case BindingExpressionParser.NullLiteral:
    170                     classType = Object.class;
    171                     break;
    172                 default:
    173                     throw new RuntimeException("cannot create expression from terminal node " +
    174                             node.toString());
    175             }
    176             return mModel.symbol(node.getText(), classType);
    177         } finally {
    178             onExit((ParserRuleContext) node.getParent().getRuleContext());
    179         }
    180     }
    181 
    182     @Override
    183     public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) {
    184         try {
    185             onEnter(ctx);
    186             return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this));
    187         } finally {
    188             onExit(ctx);
    189         }
    190     }
    191 
    192     @Override
    193     public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) {
    194         try {
    195             onEnter(ctx);
    196             return mModel.identifier(ctx.getText());
    197         } finally {
    198             onExit(ctx);
    199         }
    200     }
    201 
    202     @Override
    203     public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) {
    204         try {
    205             onEnter(ctx);
    206             return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this),
    207                     ctx.iffalse.accept(this));
    208         } finally {
    209             onExit(ctx);
    210         }
    211 
    212     }
    213 
    214     @Override
    215     public Expr visitMethodInvocation(
    216             @NotNull BindingExpressionParser.MethodInvocationContext ctx) {
    217         try {
    218             onEnter(ctx);
    219             List<Expr> args = new ArrayList<Expr>();
    220             if (ctx.args != null) {
    221                 for (ParseTree item : ctx.args.children) {
    222                     if (Objects.equal(item.getText(), ",")) {
    223                         continue;
    224                     }
    225                     args.add(item.accept(this));
    226                 }
    227             }
    228             return mModel.methodCall(ctx.target.accept(this),
    229                     ctx.Identifier().getText(), args);
    230         } finally {
    231             onExit(ctx);
    232         }
    233     }
    234 
    235     @Override
    236     public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) {
    237         try {
    238             onEnter(ctx);
    239             return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    240         } finally {
    241             onExit(ctx);
    242         }
    243     }
    244 
    245     @Override
    246     public Expr visitAndOrOp(@NotNull AndOrOpContext ctx) {
    247         try {
    248             onEnter(ctx);
    249             return mModel.logical(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    250         } finally {
    251             onExit(ctx);
    252         }
    253     }
    254 
    255     @Override
    256     public Expr visitBinaryOp(@NotNull BinaryOpContext ctx) {
    257         try {
    258             onEnter(ctx);
    259             return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    260         } finally {
    261             onExit(ctx);
    262         }
    263     }
    264 
    265     @Override
    266     public Expr visitBitShiftOp(@NotNull BitShiftOpContext ctx) {
    267         try {
    268             onEnter(ctx);
    269             return mModel.bitshift(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
    270         } finally {
    271             onExit(ctx);
    272         }
    273     }
    274 
    275     @Override
    276     public Expr visitInstanceOfOp(@NotNull InstanceOfOpContext ctx) {
    277         try {
    278             onEnter(ctx);
    279             return mModel.instanceOfOp(ctx.expression().accept(this), ctx.type().getText());
    280         } finally {
    281             onExit(ctx);
    282         }
    283     }
    284 
    285     @Override
    286     public Expr visitUnaryOp(@NotNull UnaryOpContext ctx) {
    287         try {
    288             onEnter(ctx);
    289             return mModel.unary(ctx.op.getText(), ctx.expression().accept(this));
    290         } finally {
    291             onExit(ctx);
    292         }
    293     }
    294 
    295     @Override
    296     public Expr visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) {
    297         try {
    298             onEnter(ctx);
    299             final List<Expr> args = new ArrayList<Expr>();
    300             if (ctx.resourceParameters() != null) {
    301                 for (ParseTree item : ctx.resourceParameters().expressionList().children) {
    302                     if (Objects.equal(item.getText(), ",")) {
    303                         continue;
    304                     }
    305                     args.add(item.accept(this));
    306                 }
    307             }
    308             final String resourceReference = ctx.ResourceReference().getText();
    309             final int colonIndex = resourceReference.indexOf(':');
    310             final int slashIndex = resourceReference.indexOf('/');
    311             final String packageName = colonIndex < 0 ? null :
    312                     resourceReference.substring(1, colonIndex).trim();
    313             final int startIndex = Math.max(1, colonIndex + 1);
    314             final String resourceType = resourceReference.substring(startIndex, slashIndex).trim();
    315             final String resourceName = resourceReference.substring(slashIndex + 1).trim();
    316             return mModel.resourceExpr(packageName, resourceType, resourceName, args);
    317         } finally {
    318             onExit(ctx);
    319         }
    320     }
    321 
    322     @Override
    323     public Expr visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) {
    324         try {
    325             onEnter(ctx);
    326             return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1)));
    327         } finally {
    328             onExit(ctx);
    329         }
    330     }
    331 
    332     @Override
    333     public Expr visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) {
    334         try {
    335             onEnter(ctx);
    336             return mModel.castExpr(ctx.type().getText(), visit(ctx.expression()));
    337         } finally {
    338             onExit(ctx);
    339         }
    340     }
    341 }
    342