• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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