1/*
2 * Copyright (C) 2016 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.expr;
18
19import android.databinding.tool.reflection.ModelAnalyzer;
20import android.databinding.tool.reflection.ModelClass;
21import android.databinding.tool.solver.ExecutionPath;
22import android.databinding.tool.writer.KCode;
23
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * This is used by inverse field access expressions to assign back to the field.
29 * For example, <code>&commat;={a.b}</code> is inverted to @{code a.b = value;}
30 */
31public class FieldAssignmentExpr extends Expr {
32    final String mName;
33
34    public FieldAssignmentExpr(Expr target, String name, Expr value) {
35        super(target, value);
36        mName = name;
37    }
38
39    @Override
40    protected String computeUniqueKey() {
41        return join(getTarget().getUniqueKey(), mName, "=", getValueExpr().getUniqueKey());
42    }
43
44    public Expr getTarget() {
45        return (FieldAccessExpr) getChildren().get(0);
46    }
47
48    public Expr getValueExpr() {
49        return getChildren().get(1);
50    }
51
52    @Override
53    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
54        return modelAnalyzer.findClass(void.class);
55    }
56
57    @Override
58    protected List<Dependency> constructDependencies() {
59        return constructDynamicChildrenDependencies();
60    }
61
62    @Override
63    protected KCode generateCode() {
64        return new KCode()
65                .app("", getTarget().toCode())
66                .app("." + mName + " = ", getValueExpr().toCode());
67    }
68
69    @Override
70    public Expr cloneToModel(ExprModel model) {
71        return model.assignment(getTarget().cloneToModel(model), mName, getValueExpr());
72    }
73
74    @Override
75    protected String getInvertibleError() {
76        return "Assignment expressions are inverses of field access expressions.";
77    }
78
79    @Override
80    public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
81        Expr child = getTarget();
82        List<ExecutionPath> targetPaths = child.toExecutionPath(paths);
83
84        // after this, we need a null check.
85        List<ExecutionPath> result = new ArrayList<ExecutionPath>();
86        if (child instanceof StaticIdentifierExpr) {
87            result.addAll(toExecutionPathInOrder(paths, child));
88        } else {
89            for (ExecutionPath path : targetPaths) {
90                final ComparisonExpr cmp = getModel()
91                        .comparison("!=", child, getModel().symbol("null", Object.class));
92                path.addPath(cmp);
93                final ExecutionPath subPath = path.addBranch(cmp, true);
94                if (subPath != null) {
95                    subPath.addPath(this);
96                    result.add(subPath);
97                }
98            }
99        }
100        return result;
101    }
102
103    @Override
104    public String toString() {
105        return getTarget().toString() + '.' + mName + " = " + getValueExpr();
106    }
107}
108