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