Binding.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.processing.ErrorMessages; 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.SetterStore; 27import android.databinding.tool.store.SetterStore.SetterCall; 28import android.databinding.tool.util.L; 29import android.databinding.tool.writer.LayoutBinderWriterKt; 30 31import java.util.List; 32 33public class Binding implements LocationScopeProvider { 34 35 private final String mName; 36 private Expr mExpr; 37 private final BindingTarget mTarget; 38 private SetterStore.SetterCall mSetterCall; 39 40 public Binding(BindingTarget target, String name, Expr expr) { 41 mTarget = target; 42 mName = name; 43 mExpr = expr; 44 } 45 46 @Override 47 public List<Location> provideScopeLocation() { 48 return mExpr.getLocations(); 49 } 50 51 public void resolveListeners() { 52 final ModelClass listenerParameter = getListenerParameter(mTarget, mName, mExpr); 53 Expr listenerExpr = mExpr.resolveListeners(listenerParameter, null); 54 if (listenerExpr != mExpr) { 55 listenerExpr.setBindingExpression(true); 56 mExpr = listenerExpr; 57 } 58 } 59 60 private SetterStore.BindingSetterCall getSetterCall() { 61 if (mSetterCall == null) { 62 try { 63 Scope.enter(getTarget()); 64 Scope.enter(this); 65 resolveSetterCall(); 66 if (mSetterCall == null) { 67 L.e(ErrorMessages.CANNOT_FIND_SETTER_CALL, mName, mExpr.getResolvedType()); 68 } 69 } finally { 70 Scope.exit(); 71 Scope.exit(); 72 } 73 } 74 return mSetterCall; 75 } 76 77 private void resolveSetterCall() { 78 ModelClass viewType = mTarget.getResolvedType(); 79 if (viewType != null && viewType.extendsViewStub()) { 80 if (isListenerAttribute(mName)) { 81 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 82 ModelClass viewStubProxy = modelAnalyzer. 83 findClass("android.databinding.ViewStubProxy", null); 84 mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName, 85 viewStubProxy, mExpr.getResolvedType(), mExpr.getModel().getImports()); 86 } else if (isViewStubAttribute(mName)) { 87 mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr); 88 } else { 89 mSetterCall = new ViewStubSetterCall(mName); 90 } 91 } else { 92 final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance()); 93 mSetterCall = setterStore.getSetterCall(mName, 94 viewType, mExpr.getResolvedType(), mExpr.getModel().getImports()); 95 } 96 } 97 98 /** 99 * Similar to getSetterCall, but assumes an Object parameter to find the best matching listener. 100 */ 101 private static ModelClass getListenerParameter(BindingTarget target, String name, Expr expr) { 102 ModelClass viewType = target.getResolvedType(); 103 SetterCall setterCall; 104 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 105 ModelClass objectParameter = modelAnalyzer.findClass(Object.class); 106 SetterStore setterStore = SetterStore.get(modelAnalyzer); 107 if (viewType != null && viewType.extendsViewStub()) { 108 if (isListenerAttribute(name)) { 109 ModelClass viewStubProxy = modelAnalyzer. 110 findClass("android.databinding.ViewStubProxy", null); 111 setterCall = SetterStore.get(modelAnalyzer).getSetterCall(name, 112 viewStubProxy, objectParameter, expr.getModel().getImports()); 113 } else if (isViewStubAttribute(name)) { 114 setterCall = new ViewStubDirectCall(name, viewType, expr); 115 } else { 116 setterCall = new ViewStubSetterCall(name); 117 } 118 } else { 119 setterCall = setterStore.getSetterCall(name, viewType, objectParameter, 120 expr.getModel().getImports()); 121 } 122 if (setterCall != null) { 123 return setterCall.getParameterTypes()[0]; 124 } 125 List<SetterStore.MultiAttributeSetter> setters = 126 setterStore.getMultiAttributeSetterCalls(new String[]{name}, viewType, 127 new ModelClass[] {modelAnalyzer.findClass(Object.class)}); 128 if (setters.isEmpty()) { 129 return null; 130 } else { 131 return setters.get(0).getParameterTypes()[0]; 132 } 133 } 134 135 public BindingTarget getTarget() { 136 return mTarget; 137 } 138 139 public String toJavaCode(String targetViewName, String bindingComponent) { 140 final String currentValue = requiresOldValue() 141 ? "this." + LayoutBinderWriterKt.getOldValueName(mExpr) : null; 142 final String argCode = getExpr().toCode().generate(); 143 return getSetterCall().toJava(bindingComponent, targetViewName, currentValue, argCode); 144 } 145 146 public String getBindingAdapterInstanceClass() { 147 return getSetterCall().getBindingAdapterInstanceClass(); 148 } 149 150 public Expr[] getComponentExpressions() { 151 return new Expr[] { mExpr }; 152 } 153 154 public boolean requiresOldValue() { 155 return getSetterCall().requiresOldValue(); 156 } 157 158 /** 159 * The min api level in which this binding should be executed. 160 * <p> 161 * This should be the minimum value among the dependencies of this binding. For now, we only 162 * check the setter. 163 */ 164 public int getMinApi() { 165 return getSetterCall().getMinApi(); 166 } 167 168 public String getName() { 169 return mName; 170 } 171 172 public Expr getExpr() { 173 return mExpr; 174 } 175 176 private static boolean isViewStubAttribute(String name) { 177 return ("android:inflatedId".equals(name) || 178 "android:layout".equals(name) || 179 "android:visibility".equals(name) || 180 "android:layoutInflater".equals(name)); 181 } 182 183 private static boolean isListenerAttribute(String name) { 184 return ("android:onInflate".equals(name) || 185 "android:onInflateListener".equals(name)); 186 } 187 188 private static class ViewStubSetterCall extends SetterCall { 189 private final String mName; 190 191 public ViewStubSetterCall(String name) { 192 mName = name.substring(name.lastIndexOf(':') + 1); 193 } 194 195 @Override 196 protected String toJavaInternal(String componentExpression, String viewExpression, 197 String converted) { 198 return "if (" + viewExpression + ".isInflated()) " + viewExpression + 199 ".getBinding().setVariable(BR." + mName + ", " + converted + ")"; 200 } 201 202 @Override 203 protected String toJavaInternal(String componentExpression, String viewExpression, 204 String oldValue, String converted) { 205 return null; 206 } 207 208 @Override 209 public int getMinApi() { 210 return 0; 211 } 212 213 @Override 214 public boolean requiresOldValue() { 215 return false; 216 } 217 218 @Override 219 public ModelClass[] getParameterTypes() { 220 return new ModelClass[] { 221 ModelAnalyzer.getInstance().findClass(Object.class) 222 }; 223 } 224 225 @Override 226 public String getBindingAdapterInstanceClass() { 227 return null; 228 } 229 } 230 231 private static class ViewStubDirectCall extends SetterCall { 232 private final SetterCall mWrappedCall; 233 234 public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) { 235 mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name, 236 viewType, expr.getResolvedType(), expr.getModel().getImports()); 237 if (mWrappedCall == null) { 238 L.e("Cannot find the setter for attribute '%s' on %s with parameter type %s.", 239 name, viewType, expr.getResolvedType()); 240 } 241 } 242 243 @Override 244 protected String toJavaInternal(String componentExpression, String viewExpression, 245 String converted) { 246 return "if (!" + viewExpression + ".isInflated()) " + 247 mWrappedCall.toJava(componentExpression, viewExpression + ".getViewStub()", 248 null, converted); 249 } 250 251 @Override 252 protected String toJavaInternal(String componentExpression, String viewExpression, 253 String oldValue, String converted) { 254 return null; 255 } 256 257 @Override 258 public int getMinApi() { 259 return 0; 260 } 261 262 @Override 263 public boolean requiresOldValue() { 264 return false; 265 } 266 267 @Override 268 public ModelClass[] getParameterTypes() { 269 return new ModelClass[] { 270 ModelAnalyzer.getInstance().findClass(Object.class) 271 }; 272 } 273 274 @Override 275 public String getBindingAdapterInstanceClass() { 276 return mWrappedCall.getBindingAdapterInstanceClass(); 277 } 278 } 279} 280