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 } 62 getInterfaceType()63 public String getInterfaceType() { 64 return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType(); 65 } 66 addInverseBinding(String name, Expr expr, String bindingClass)67 public InverseBinding addInverseBinding(String name, Expr expr, String bindingClass) { 68 expr.assertIsInvertible(); 69 final InverseBinding inverseBinding = new InverseBinding(this, name, expr, bindingClass); 70 mInverseBindings.add(inverseBinding); 71 mBindings.add(new Binding(this, inverseBinding.getEventAttribute(), 72 mModel.twoWayListenerExpr(inverseBinding), 73 inverseBinding.getEventSetter())); 74 return inverseBinding; 75 } 76 addInverseBinding(String name, BindingGetterCall call)77 public InverseBinding addInverseBinding(String name, BindingGetterCall call) { 78 final InverseBinding inverseBinding = new InverseBinding(this, name, call); 79 mInverseBindings.add(inverseBinding); 80 mBindings.add(new Binding(this, inverseBinding.getEventAttribute(), 81 mModel.twoWayListenerExpr(inverseBinding))); 82 return inverseBinding; 83 } 84 85 @Override provideScopeLocation()86 public List<Location> provideScopeLocation() { 87 return mBundle.provideScopeLocation(); 88 } 89 getId()90 public String getId() { 91 return mBundle.getId(); 92 } 93 getTag()94 public String getTag() { 95 return mBundle.getTag(); 96 } 97 getOriginalTag()98 public String getOriginalTag() { 99 return mBundle.getOriginalTag(); 100 } 101 getViewClass()102 public String getViewClass() { 103 return mBundle.getFullClassName(); 104 } 105 getResolvedType()106 public ModelClass getResolvedType() { 107 if (mResolvedClass == null) { 108 if (mBundle.isBinder()) { 109 mResolvedClass = ModelAnalyzer.getInstance(). 110 findClass(mBundle.getInterfaceType(), mModel.getImports()); 111 } else { 112 mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(), 113 mModel.getImports()); 114 } 115 } 116 return mResolvedClass; 117 } 118 getIncludedLayout()119 public String getIncludedLayout() { 120 return mBundle.getIncludedLayout(); 121 } 122 isBinder()123 public boolean isBinder() { 124 return getIncludedLayout() != null; 125 } 126 supportsTag()127 public boolean supportsTag() { 128 return !SetterStore.get(ModelAnalyzer.getInstance()) 129 .isUntaggable(mBundle.getFullClassName()); 130 } 131 getBindings()132 public List<Binding> getBindings() { 133 return mBindings; 134 } 135 getInverseBindings()136 public List<InverseBinding> getInverseBindings() { 137 return mInverseBindings; 138 } 139 getModel()140 public ExprModel getModel() { 141 return mModel; 142 } 143 setModel(ExprModel model)144 public void setModel(ExprModel model) { 145 mModel = model; 146 } 147 resolveListeners()148 public void resolveListeners() { 149 for (Binding binding : mBindings) { 150 try { 151 Scope.enter(binding); 152 binding.resolveListeners(); 153 } finally { 154 Scope.exit(); 155 } 156 } 157 } 158 resolveCallbackParams()159 public void resolveCallbackParams() { 160 for (Binding binding : mBindings) { 161 try { 162 Scope.enter(binding); 163 binding.resolveCallbackParams(); 164 } finally { 165 Scope.exit(); 166 } 167 } 168 } 169 resolveTwoWayExpressions()170 public void resolveTwoWayExpressions() { 171 for (Binding binding : mBindings) { 172 try { 173 Scope.enter(binding); 174 binding.resolveTwoWayExpressions(); 175 } finally { 176 Scope.exit(); 177 } 178 } 179 } 180 181 /** 182 * Called after BindingTarget is finalized. 183 * <p> 184 * We traverse all bindings and ask SetterStore to figure out if any can be combined. 185 * When N bindings are combined, they are demoted from being a binding expression and a new 186 * ArgList expression is added as the new binding expression that depends on others. 187 */ resolveMultiSetters()188 public void resolveMultiSetters() { 189 L.d("resolving multi setters for %s", getId()); 190 final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance()); 191 final String[] attributes = new String[mBindings.size()]; 192 final ModelClass[] types = new ModelClass[mBindings.size()]; 193 for (int i = 0; i < mBindings.size(); i ++) { 194 Binding binding = mBindings.get(i); 195 try { 196 Scope.enter(binding); 197 attributes[i] = binding.getName(); 198 types[i] = binding.getExpr().getResolvedType(); 199 } finally { 200 Scope.exit(); 201 } 202 } 203 final List<SetterStore.MultiAttributeSetter> multiAttributeSetterCalls = setterStore 204 .getMultiAttributeSetterCalls(attributes, getResolvedType(), types); 205 if (multiAttributeSetterCalls.isEmpty()) { 206 return; 207 } 208 final Map<String, Binding> lookup = new HashMap<String, Binding>(); 209 for (Binding binding : mBindings) { 210 String name = binding.getName(); 211 if (name.startsWith("android:")) { 212 lookup.put(name, binding); 213 } else { 214 int ind = name.indexOf(":"); 215 if (ind == -1) { 216 lookup.put(name, binding); 217 } else { 218 lookup.put(name.substring(ind + 1), binding); 219 } 220 } 221 } 222 List<MergedBinding> mergeBindings = new ArrayList<MergedBinding>(); 223 for (final SetterStore.MultiAttributeSetter setter : multiAttributeSetterCalls) { 224 L.d("resolved %s", setter); 225 final List<Binding> mergedBindings = new ArrayList<Binding>(); 226 for (String attribute : setter.attributes) { 227 Binding binding = lookup.get(attribute); 228 Preconditions.checkNotNull(binding, "cannot find binding for %s", attribute); 229 mergedBindings.add(binding); 230 } 231 232 for (Binding binding : mergedBindings) { 233 binding.getExpr().setBindingExpression(false); 234 mBindings.remove(binding); 235 } 236 MergedBinding mergedBinding = new MergedBinding(getModel(), setter, this, 237 mergedBindings); 238 mergeBindings.add(mergedBinding); 239 } 240 for (MergedBinding binding : mergeBindings) { 241 mBindings.add(binding); 242 } 243 } 244 } 245