Binding.java revision 731b74f7f44e67312a1fc4161c4e0aae221b2417
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.CodeGenUtil; 30import android.databinding.tool.writer.WriterPackage; 31 32import java.util.List; 33 34public 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 41 public Binding(BindingTarget target, String name, Expr expr) { 42 mTarget = target; 43 mName = name; 44 mExpr = expr; 45 } 46 47 @Override 48 public List<Location> provideScopeLocation() { 49 return mExpr.getLocations(); 50 } 51 52 public void resolveListeners() { 53 ModelClass listenerParameter = getListenerParameter(); 54 if (listenerParameter != null) { 55 mExpr.resolveListeners(listenerParameter); 56 } 57 } 58 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 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 */ 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 126 public BindingTarget getTarget() { 127 return mTarget; 128 } 129 130 public String toJavaCode(String targetViewName) { 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(targetViewName, currentValue, argCode); 135 } 136 137 public Expr[] getComponentExpressions() { 138 return new Expr[] { mExpr }; 139 } 140 141 public boolean requiresOldValue() { 142 return getSetterCall().requiresOldValue(); 143 } 144 145 /** 146 * The min api level in which this binding should be executed. 147 * <p> 148 * This should be the minimum value among the dependencies of this binding. For now, we only 149 * check the setter. 150 */ 151 public int getMinApi() { 152 return getSetterCall().getMinApi(); 153 } 154 155 public String getName() { 156 return mName; 157 } 158 159 public Expr getExpr() { 160 return mExpr; 161 } 162 163 private boolean isViewStubAttribute() { 164 return ("android:inflatedId".equals(mName) || 165 "android:layout".equals(mName) || 166 "android:visibility".equals(mName) || 167 "android:layoutInflater".equals(mName)); 168 } 169 170 private boolean isListenerAttribute() { 171 return ("android:onInflate".equals(mName) || 172 "android:onInflateListener".equals(mName)); 173 174 } 175 176 private static class ViewStubSetterCall extends SetterCall { 177 private final String mName; 178 179 public ViewStubSetterCall(String name) { 180 mName = name.substring(name.lastIndexOf(':') + 1); 181 } 182 183 @Override 184 protected String toJavaInternal(String viewExpression, String converted) { 185 return "if (" + viewExpression + ".isInflated()) " + viewExpression + 186 ".getBinding().setVariable(BR." + mName + ", " + converted + ")"; 187 } 188 189 @Override 190 protected String toJavaInternal(String viewExpression, String oldValue, String converted) { 191 return null; 192 } 193 194 @Override 195 public int getMinApi() { 196 return 0; 197 } 198 199 @Override 200 public boolean requiresOldValue() { 201 return false; 202 } 203 204 @Override 205 public ModelClass[] getParameterTypes() { 206 return new ModelClass[] { 207 ModelAnalyzer.getInstance().findClass(Object.class) 208 }; 209 } 210 } 211 212 private static class ViewStubDirectCall extends SetterCall { 213 private final SetterCall mWrappedCall; 214 215 public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) { 216 mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name, 217 viewType, expr.getResolvedType(), expr.getModel().getImports()); 218 if (mWrappedCall == null) { 219 L.e("Cannot find the setter for attribute '%s' on %s with parameter type %s.", 220 name, viewType, expr.getResolvedType()); 221 } 222 } 223 224 @Override 225 protected String toJavaInternal(String viewExpression, String converted) { 226 return "if (!" + viewExpression + ".isInflated()) " + 227 mWrappedCall.toJava(viewExpression + ".getViewStub()", null, converted); 228 } 229 230 @Override 231 protected String toJavaInternal(String viewExpression, String oldValue, String converted) { 232 return null; 233 } 234 235 @Override 236 public int getMinApi() { 237 return 0; 238 } 239 240 @Override 241 public boolean requiresOldValue() { 242 return false; 243 } 244 245 @Override 246 public ModelClass[] getParameterTypes() { 247 return new ModelClass[] { 248 ModelAnalyzer.getInstance().findClass(Object.class) 249 }; 250 } 251 } 252} 253