BindingTarget.java revision 731b74f7f44e67312a1fc4161c4e0aae221b2417
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.tool.expr.Expr;
20import android.databinding.tool.expr.ExprModel;
21import android.databinding.tool.processing.Scope;
22import android.databinding.tool.processing.scopes.FileScopeProvider;
23import android.databinding.tool.processing.scopes.LocationScopeProvider;
24import android.databinding.tool.reflection.ModelAnalyzer;
25import android.databinding.tool.reflection.ModelClass;
26import android.databinding.tool.store.Location;
27import android.databinding.tool.store.ResourceBundle;
28import android.databinding.tool.store.SetterStore;
29import android.databinding.tool.util.L;
30import android.databinding.tool.util.Preconditions;
31
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
37
38public class BindingTarget implements LocationScopeProvider {
39    List<Binding> mBindings = new ArrayList<Binding>();
40    ExprModel mModel;
41    ModelClass mResolvedClass;
42
43    // if this target presents itself in multiple layout files with different view types,
44    // it receives an interface type and should use it in the getter instead.
45    private ResourceBundle.BindingTargetBundle mBundle;
46
47    public BindingTarget(ResourceBundle.BindingTargetBundle bundle) {
48        mBundle = bundle;
49    }
50
51    public boolean isUsed() {
52        return mBundle.isUsed();
53    }
54
55    public void addBinding(String name, Expr expr) {
56        mBindings.add(new Binding(this, name, expr));
57    }
58
59    public String getInterfaceType() {
60        return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
61    }
62
63    @Override
64    public List<Location> provideScopeLocation() {
65        return mBundle.provideScopeLocation();
66    }
67
68    public String getId() {
69        return mBundle.getId();
70    }
71
72    public String getTag() {
73        return mBundle.getTag();
74    }
75
76    public String getOriginalTag() {
77        return mBundle.getOriginalTag();
78    }
79
80    public String getViewClass() {
81        return mBundle.getFullClassName();
82    }
83
84    public ModelClass getResolvedType() {
85        if (mResolvedClass == null) {
86            mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
87                    mModel.getImports());
88        }
89        return mResolvedClass;
90    }
91
92    public String getIncludedLayout() {
93        return mBundle.getIncludedLayout();
94    }
95
96    public boolean isBinder() {
97        return getIncludedLayout() != null;
98    }
99
100    public boolean supportsTag() {
101        return !SetterStore.get(ModelAnalyzer.getInstance())
102                .isUntaggable(mBundle.getFullClassName());
103    }
104
105    public List<Binding> getBindings() {
106        return mBindings;
107    }
108
109    public ExprModel getModel() {
110        return mModel;
111    }
112
113    public void setModel(ExprModel model) {
114        mModel = model;
115    }
116
117    /**
118     * Called after BindingTarget is finalized.
119     * <p>
120     * We traverse all bindings and ask SetterStore to figure out if any can be combined.
121     * When N bindings are combined, they are demoted from being a binding expression and a new
122     * ArgList expression is added as the new binding expression that depends on others.
123     */
124    public void resolveMultiSetters() {
125        L.d("resolving multi setters for %s", getId());
126        final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
127        final String[] attributes = new String[mBindings.size()];
128        final ModelClass[] types = new ModelClass[mBindings.size()];
129        for (int i = 0; i < mBindings.size(); i ++) {
130            Binding binding = mBindings.get(i);
131            try {
132                Scope.enter(binding);
133                attributes[i] = binding.getName();
134                types[i] = binding.getExpr().getResolvedType();
135            } finally {
136                Scope.exit();
137            }
138        }
139        final List<SetterStore.MultiAttributeSetter> multiAttributeSetterCalls = setterStore
140                .getMultiAttributeSetterCalls(attributes, getResolvedType(), types);
141        if (multiAttributeSetterCalls.isEmpty()) {
142            return;
143        }
144        final Map<String, Binding> lookup = new HashMap<String, Binding>();
145        for (Binding binding : mBindings) {
146            String name = binding.getName();
147            if (name.startsWith("android:")) {
148                lookup.put(name, binding);
149            } else {
150                int ind = name.indexOf(":");
151                if (ind == -1) {
152                    lookup.put(name, binding);
153                } else {
154                    lookup.put(name.substring(ind + 1), binding);
155                }
156            }
157        }
158        List<MergedBinding> mergeBindings = new ArrayList<MergedBinding>();
159        for (final SetterStore.MultiAttributeSetter setter : multiAttributeSetterCalls) {
160            L.d("resolved %s", setter);
161            final List<Binding> mergedBindings = new ArrayList<>();
162            for (String attribute : setter.attributes) {
163                Binding binding = lookup.get(attribute);
164                Preconditions.checkNotNull(binding, "cannot find binding for %s", attribute);
165                mergedBindings.add(binding);
166            }
167
168            for (Binding binding : mergedBindings) {
169                binding.getExpr().setBindingExpression(false);
170                mBindings.remove(binding);
171            }
172            MergedBinding mergedBinding = new MergedBinding(getModel(), setter, this,
173                    mergedBindings);
174            mergeBindings.add(mergedBinding);
175        }
176        for (MergedBinding binding : mergeBindings) {
177            mBindings.add(binding);
178        }
179    }
180}
181