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.expr.ExprModel; 21 import android.databinding.tool.expr.FieldAccessExpr; 22 import android.databinding.tool.processing.ErrorMessages; 23 import android.databinding.tool.processing.Scope; 24 import android.databinding.tool.processing.scopes.LocationScopeProvider; 25 import android.databinding.tool.reflection.ModelAnalyzer; 26 import android.databinding.tool.reflection.ModelClass; 27 import android.databinding.tool.store.Location; 28 import android.databinding.tool.store.SetterStore; 29 import android.databinding.tool.store.SetterStore.BindingGetterCall; 30 import android.databinding.tool.store.SetterStore.BindingSetterCall; 31 import android.databinding.tool.util.L; 32 import android.databinding.tool.util.Preconditions; 33 import android.databinding.tool.writer.FlagSet; 34 import android.databinding.tool.writer.KCode; 35 import android.databinding.tool.writer.LayoutBinderWriterKt; 36 37 import kotlin.jvm.functions.Function2; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 public class InverseBinding implements LocationScopeProvider { 43 44 private final String mName; 45 private final Expr mExpr; 46 private final BindingTarget mTarget; 47 private BindingGetterCall mGetterCall; 48 private final ArrayList<FieldAccessExpr> mChainedExpressions = new ArrayList<FieldAccessExpr>(); 49 InverseBinding(BindingTarget target, String name, Expr expr)50 public InverseBinding(BindingTarget target, String name, Expr expr) { 51 mTarget = target; 52 mName = name; 53 mExpr = expr; 54 } 55 56 @Override provideScopeLocation()57 public List<Location> provideScopeLocation() { 58 if (mExpr != null) { 59 return mExpr.getLocations(); 60 } else { 61 return mChainedExpressions.get(0).getLocations(); 62 } 63 } 64 setGetterCall(BindingGetterCall getterCall)65 void setGetterCall(BindingGetterCall getterCall) { 66 mGetterCall = getterCall; 67 } 68 addChainedExpression(FieldAccessExpr expr)69 public void addChainedExpression(FieldAccessExpr expr) { 70 mChainedExpressions.add(expr); 71 } 72 isOnBinder()73 public boolean isOnBinder() { 74 return mTarget.getResolvedType().isViewDataBinding(); 75 } 76 getGetterCall()77 private SetterStore.BindingGetterCall getGetterCall() { 78 if (mGetterCall == null) { 79 if (mExpr != null) { 80 mExpr.getResolvedType(); // force resolve of ObservableFields 81 } 82 try { 83 Scope.enter(mTarget); 84 Scope.enter(this); 85 resolveGetterCall(); 86 if (mGetterCall == null) { 87 L.e(ErrorMessages.CANNOT_FIND_GETTER_CALL, mName, 88 mExpr == null ? "Unknown" : mExpr.getResolvedType(), 89 mTarget.getResolvedType()); 90 } 91 } finally { 92 Scope.exit(); 93 Scope.exit(); 94 } 95 } 96 return mGetterCall; 97 } 98 resolveGetterCall()99 private void resolveGetterCall() { 100 ModelClass viewType = mTarget.getResolvedType(); 101 final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance()); 102 final ModelClass resolvedType = mExpr == null ? null : mExpr.getResolvedType(); 103 mGetterCall = setterStore.getGetterCall(mName, viewType, resolvedType, 104 getModel().getImports()); 105 } 106 getTarget()107 public BindingTarget getTarget() { 108 return mTarget; 109 } 110 toJavaCode(String bindingComponent, final FlagSet flagField)111 public KCode toJavaCode(String bindingComponent, final FlagSet flagField) { 112 final String targetViewName = LayoutBinderWriterKt.getFieldName(getTarget()); 113 KCode code = new KCode(); 114 // A chained expression will have substituted its chained value for the expression 115 // unless the attribute has no expression. Therefore, chaining and expressions are 116 // mutually exclusive. 117 Preconditions.check((mExpr == null) != mChainedExpressions.isEmpty(), 118 "Chained expressions are only against unbound attributes."); 119 if (mExpr != null) { 120 code.app("", mExpr.toInverseCode(new KCode(getGetterCall().toJava(bindingComponent, 121 targetViewName)))); 122 } else { // !mChainedExpressions.isEmpty()) 123 final String fieldName = flagField.getLocalName(); 124 FlagSet flagSet = new FlagSet(); 125 for (FieldAccessExpr expr : mChainedExpressions) { 126 flagSet = flagSet.or(new FlagSet(expr.getId())); 127 } 128 final FlagSet allFlags = flagSet; 129 code.nl(new KCode("synchronized(this) {")); 130 code.tab(LayoutBinderWriterKt 131 .mapOr(flagField, flagSet, new Function2<String, Integer, KCode>() { 132 @Override 133 public KCode invoke(String suffix, Integer index) { 134 return new KCode(fieldName) 135 .app(suffix) 136 .app(" |= ") 137 .app(LayoutBinderWriterKt.binaryCode(allFlags, index)) 138 .app(";"); 139 } 140 })); 141 code.nl(new KCode("}")); 142 code.nl(new KCode("requestRebind()")); 143 } 144 return code; 145 } 146 getBindingAdapterInstanceClass()147 public String getBindingAdapterInstanceClass() { 148 return getGetterCall().getBindingAdapterInstanceClass(); 149 } 150 151 /** 152 * The min api level in which this binding should be executed. 153 * <p> 154 * This should be the minimum value among the dependencies of this binding. 155 */ getMinApi()156 public int getMinApi() { 157 final BindingGetterCall getterCall = getGetterCall(); 158 return Math.max(getterCall.getMinApi(), getterCall.getEvent().getMinApi()); 159 } 160 getEventSetter()161 public BindingSetterCall getEventSetter() { 162 final BindingGetterCall getterCall = getGetterCall(); 163 return getterCall.getEvent(); 164 } 165 getName()166 public String getName() { 167 return mName; 168 } 169 getEventAttribute()170 public String getEventAttribute() { 171 return getGetterCall().getEventAttribute(); 172 } 173 getModel()174 public ExprModel getModel() { 175 if (mExpr != null) { 176 return mExpr.getModel(); 177 } 178 return mChainedExpressions.get(0).getModel(); 179 } 180 } 181