• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ExpressionVisitor(ExprModel model)52     ExpressionVisitor(ExprModel model) {
53         mModel = model;
54     }
55 
setParseTreeListener(ParseTreeListener parseTreeListener)56     void setParseTreeListener(ParseTreeListener parseTreeListener) {
57         mParseTreeListener = parseTreeListener;
58     }
59 
setBindingTarget(BindingTarget bindingTarget)60     public void setBindingTarget(BindingTarget bindingTarget) {
61         mTarget = bindingTarget;
62     }
63 
onEnter(ParserRuleContext context)64     private void onEnter(ParserRuleContext context) {
65         if (mParseTreeListener != null) {
66             mParseTreeListener.enterEveryRule(context);
67         }
68     }
69 
onExit(ParserRuleContext context)70     private void onExit(ParserRuleContext context) {
71         if (mParseTreeListener != null) {
72             mParseTreeListener.exitEveryRule(context);
73         }
74     }
75 
pushModel(ExprModel model)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 
popModel()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
visitRootLambda(@onNull BindingExpressionParser.RootLambdaContext ctx)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
visitSingleLambdaParameter( @onNull BindingExpressionParser.SingleLambdaParameterContext ctx)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
visitLambdaParameterList( @onNull BindingExpressionParser.LambdaParameterListContext ctx)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
visitStringLiteral(@onNull BindingExpressionParser.StringLiteralContext ctx)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
visitRootExpr(@onNull BindingExpressionParser.RootExprContext ctx)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
visitGrouping(@onNull BindingExpressionParser.GroupingContext ctx)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
visitDotOp(@onNull BindingExpressionParser.DotOpContext ctx)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
visitFunctionRef(@onNull BindingExpressionParser.FunctionRefContext ctx)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
visitQuestionQuestionOp( @onNull BindingExpressionParser.QuestionQuestionOpContext ctx)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
visitTerminal(@onNull TerminalNode node)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
visitComparisonOp(@onNull BindingExpressionParser.ComparisonOpContext ctx)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
visitIdentifier(@onNull BindingExpressionParser.IdentifierContext ctx)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
visitTernaryOp(@onNull BindingExpressionParser.TernaryOpContext ctx)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
visitMethodInvocation( @onNull BindingExpressionParser.MethodInvocationContext ctx)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
visitMathOp(@onNull BindingExpressionParser.MathOpContext ctx)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
visitAndOrOp(@onNull AndOrOpContext ctx)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
visitBinaryOp(@onNull BinaryOpContext ctx)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
visitBitShiftOp(@onNull BitShiftOpContext ctx)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
visitInstanceOfOp(@onNull InstanceOfOpContext ctx)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
visitUnaryOp(@onNull UnaryOpContext ctx)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
visitResources(@onNull BindingExpressionParser.ResourcesContext ctx)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