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