/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.databinding.tool; import android.databinding.tool.expr.Expr; import android.databinding.tool.processing.ErrorMessages; import android.databinding.tool.processing.Scope; import android.databinding.tool.processing.scopes.LocationScopeProvider; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.store.Location; import android.databinding.tool.store.SetterStore; import android.databinding.tool.store.SetterStore.SetterCall; import android.databinding.tool.util.L; import android.databinding.tool.writer.CodeGenUtil; import android.databinding.tool.writer.WriterPackage; import java.util.List; public class Binding implements LocationScopeProvider { private final String mName; private final Expr mExpr; private final BindingTarget mTarget; private SetterStore.SetterCall mSetterCall; public Binding(BindingTarget target, String name, Expr expr) { mTarget = target; mName = name; mExpr = expr; } @Override public List<Location> provideScopeLocation() { return mExpr.getLocations(); } public void resolveListeners() { ModelClass listenerParameter = getListenerParameter(); if (listenerParameter != null) { mExpr.resolveListeners(listenerParameter); } } private SetterStore.BindingSetterCall getSetterCall() { if (mSetterCall == null) { try { Scope.enter(getTarget()); Scope.enter(this); resolveSetterCall(); if (mSetterCall == null) { L.e(ErrorMessages.CANNOT_FIND_SETTER_CALL, mName, mExpr.getResolvedType()); } } finally { Scope.exit(); Scope.exit(); } } return mSetterCall; } private void resolveSetterCall() { ModelClass viewType = mTarget.getResolvedType(); if (viewType != null && viewType.extendsViewStub()) { if (isListenerAttribute()) { ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); ModelClass viewStubProxy = modelAnalyzer. findClass("android.databinding.ViewStubProxy", null); mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName, viewStubProxy, mExpr.getResolvedType(), mExpr.getModel().getImports()); } else if (isViewStubAttribute()) { mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr); } else { mSetterCall = new ViewStubSetterCall(mName); } } else { mSetterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName, viewType, mExpr.getResolvedType(), mExpr.getModel().getImports()); } } /** * Similar to getSetterCall, but assumes an Object parameter to find the best matching listener. */ private ModelClass getListenerParameter() { ModelClass viewType = mTarget.getResolvedType(); SetterCall setterCall; ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); ModelClass objectParameter = modelAnalyzer.findClass(Object.class); if (viewType != null && viewType.extendsViewStub()) { if (isListenerAttribute()) { ModelClass viewStubProxy = modelAnalyzer. findClass("android.databinding.ViewStubProxy", null); setterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName, viewStubProxy, objectParameter, mExpr.getModel().getImports()); } else if (isViewStubAttribute()) { setterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName, viewType, objectParameter, mExpr.getModel().getImports()); } else { setterCall = new ViewStubSetterCall(mName); } } else { setterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName, viewType, objectParameter, mExpr.getModel().getImports()); } if (setterCall == null) { return null; } return setterCall.getParameterTypes()[0]; } public BindingTarget getTarget() { return mTarget; } public String toJavaCode(String targetViewName, String bindingComponent) { final String currentValue = requiresOldValue() ? "this." + WriterPackage.getOldValueName(mExpr) : null; final String argCode = CodeGenUtil.Companion.toCode(getExpr(), false).generate(); return getSetterCall().toJava(bindingComponent, targetViewName, currentValue, argCode); } public String getBindingAdapterInstanceClass() { return getSetterCall().getBindingAdapterInstanceClass(); } public void setBindingAdapterCall(String method) { getSetterCall().setBindingAdapterCall(method); } public Expr[] getComponentExpressions() { return new Expr[] { mExpr }; } public boolean requiresOldValue() { return getSetterCall().requiresOldValue(); } /** * The min api level in which this binding should be executed. * <p> * This should be the minimum value among the dependencies of this binding. For now, we only * check the setter. */ public int getMinApi() { return getSetterCall().getMinApi(); } public String getName() { return mName; } public Expr getExpr() { return mExpr; } private boolean isViewStubAttribute() { return ("android:inflatedId".equals(mName) || "android:layout".equals(mName) || "android:visibility".equals(mName) || "android:layoutInflater".equals(mName)); } private boolean isListenerAttribute() { return ("android:onInflate".equals(mName) || "android:onInflateListener".equals(mName)); } private static class ViewStubSetterCall extends SetterCall { private final String mName; public ViewStubSetterCall(String name) { mName = name.substring(name.lastIndexOf(':') + 1); } @Override protected String toJavaInternal(String componentExpression, String viewExpression, String converted) { return "if (" + viewExpression + ".isInflated()) " + viewExpression + ".getBinding().setVariable(BR." + mName + ", " + converted + ")"; } @Override protected String toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted) { return null; } @Override public int getMinApi() { return 0; } @Override public boolean requiresOldValue() { return false; } @Override public ModelClass[] getParameterTypes() { return new ModelClass[] { ModelAnalyzer.getInstance().findClass(Object.class) }; } @Override public String getBindingAdapterInstanceClass() { return null; } @Override public void setBindingAdapterCall(String method) { } } private static class ViewStubDirectCall extends SetterCall { private final SetterCall mWrappedCall; public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) { mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name, viewType, expr.getResolvedType(), expr.getModel().getImports()); if (mWrappedCall == null) { L.e("Cannot find the setter for attribute '%s' on %s with parameter type %s.", name, viewType, expr.getResolvedType()); } } @Override protected String toJavaInternal(String componentExpression, String viewExpression, String converted) { return "if (!" + viewExpression + ".isInflated()) " + mWrappedCall.toJava(componentExpression, viewExpression + ".getViewStub()", null, converted); } @Override protected String toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted) { return null; } @Override public int getMinApi() { return 0; } @Override public boolean requiresOldValue() { return false; } @Override public ModelClass[] getParameterTypes() { return new ModelClass[] { ModelAnalyzer.getInstance().findClass(Object.class) }; } @Override public String getBindingAdapterInstanceClass() { return mWrappedCall.getBindingAdapterInstanceClass(); } @Override public void setBindingAdapterCall(String method) { mWrappedCall.setBindingAdapterCall(method); } } }