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