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