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