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