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 17package android.databinding.tool; 18 19import android.databinding.parser.BindingExpressionBaseVisitor; 20import android.databinding.parser.BindingExpressionParser; 21import android.databinding.parser.BindingExpressionParser.AndOrOpContext; 22import android.databinding.parser.BindingExpressionParser.BinaryOpContext; 23import android.databinding.parser.BindingExpressionParser.BitShiftOpContext; 24import android.databinding.parser.BindingExpressionParser.InstanceOfOpContext; 25import android.databinding.parser.BindingExpressionParser.UnaryOpContext; 26import android.databinding.tool.expr.CallbackExprModel; 27import android.databinding.tool.expr.Expr; 28import android.databinding.tool.expr.ExprModel; 29import android.databinding.tool.expr.StaticIdentifierExpr; 30import android.databinding.tool.reflection.ModelAnalyzer; 31import android.databinding.tool.reflection.ModelClass; 32import android.databinding.tool.util.Preconditions; 33 34import com.android.annotations.NonNull; 35import com.google.common.base.Objects; 36 37import org.antlr.v4.runtime.ParserRuleContext; 38import org.antlr.v4.runtime.tree.ParseTree; 39import org.antlr.v4.runtime.tree.ParseTreeListener; 40import org.antlr.v4.runtime.tree.TerminalNode; 41 42import java.util.ArrayDeque; 43import java.util.ArrayList; 44import java.util.List; 45 46class 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