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