• 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 package android.databinding.tool.store;
17 
18 import android.databinding.InverseBindingListener;
19 import android.databinding.tool.reflection.ModelAnalyzer;
20 import android.databinding.tool.reflection.ModelClass;
21 import android.databinding.tool.reflection.ModelMethod;
22 import android.databinding.tool.util.GenerationalClassUtil;
23 import android.databinding.tool.util.L;
24 import android.databinding.tool.util.Preconditions;
25 import android.databinding.tool.util.StringUtils;
26 
27 import java.io.IOException;
28 import java.io.Serializable;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.TreeMap;
40 
41 import javax.annotation.processing.ProcessingEnvironment;
42 import javax.lang.model.element.ExecutableElement;
43 import javax.lang.model.element.Modifier;
44 import javax.lang.model.element.TypeElement;
45 import javax.lang.model.element.VariableElement;
46 import javax.lang.model.type.ArrayType;
47 import javax.lang.model.type.DeclaredType;
48 import javax.lang.model.type.TypeKind;
49 import javax.lang.model.type.TypeMirror;
50 
51 public class SetterStore {
52     private static SetterStore sStore;
53 
54     private final IntermediateV2 mStore;
55     private final ModelAnalyzer mClassAnalyzer;
56     private HashMap<String, List<String>> mInstanceAdapters;
57     private final HashSet<String> mInverseEventAttributes = new HashSet<String>();
58 
59     private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS =
60             new Comparator<MultiAttributeSetter>() {
61                 @Override
62                 public int compare(MultiAttributeSetter o1, MultiAttributeSetter o2) {
63                     if (o1.attributes.length != o2.attributes.length) {
64                         return o2.attributes.length - o1.attributes.length;
65                     }
66                     ModelClass view1 = mClassAnalyzer.findClass(o1.mKey.viewType, null).erasure();
67                     ModelClass view2 = mClassAnalyzer.findClass(o2.mKey.viewType, null).erasure();
68                     if (!view1.equals(view2)) {
69                         if (view1.isAssignableFrom(view2)) {
70                             return 1;
71                         } else {
72                             return -1;
73                         }
74                     }
75                     if (!o1.mKey.attributeIndices.keySet()
76                             .equals(o2.mKey.attributeIndices.keySet())) {
77                         // order by attribute name
78                         Iterator<String> o1Keys = o1.mKey.attributeIndices.keySet().iterator();
79                         Iterator<String> o2Keys = o2.mKey.attributeIndices.keySet().iterator();
80                         while (o1Keys.hasNext()) {
81                             String key1 = o1Keys.next();
82                             String key2 = o2Keys.next();
83                             int compare = key1.compareTo(key2);
84                             if (compare != 0) {
85                                 return compare;
86                             }
87                         }
88                         Preconditions.check(false,
89                                 "The sets don't match! That means the keys shouldn't match also");
90                     }
91                     // Same view type. Same attributes
92                     for (String attribute : o1.mKey.attributeIndices.keySet()) {
93                         final int index1 = o1.mKey.attributeIndices.get(attribute);
94                         final int index2 = o2.mKey.attributeIndices.get(attribute);
95                         ModelClass type1 = mClassAnalyzer
96                                 .findClass(o1.mKey.parameterTypes[index1], null);
97                         ModelClass type2 = mClassAnalyzer
98                                 .findClass(o2.mKey.parameterTypes[index2], null);
99                         if (type1.equals(type2)) {
100                             continue;
101                         }
102                         if (o1.mCasts[index1] != null) {
103                             if (o2.mCasts[index2] == null) {
104                                 return 1; // o2 is better
105                             } else {
106                                 continue; // both are casts
107                             }
108                         } else if (o2.mCasts[index2] != null) {
109                             return -1; // o1 is better
110                         }
111                         if (o1.mConverters[index1] != null) {
112                             if (o2.mConverters[index2] == null) {
113                                 return 1; // o2 is better
114                             } else {
115                                 continue; // both are conversions
116                             }
117                         } else if (o2.mConverters[index2] != null) {
118                             return -1; // o1 is better
119                         }
120 
121                         if (type1.isPrimitive()) {
122                             if (type2.isPrimitive()) {
123                                 int type1ConversionLevel = ModelMethod
124                                         .getImplicitConversionLevel(type1);
125                                 int type2ConversionLevel = ModelMethod
126                                         .getImplicitConversionLevel(type2);
127                                 return type2ConversionLevel - type1ConversionLevel;
128                             } else {
129                                 // type1 is primitive and has higher priority
130                                 return -1;
131                             }
132                         } else if (type2.isPrimitive()) {
133                             return 1;
134                         }
135                         if (type1.isAssignableFrom(type2)) {
136                             return 1;
137                         } else if (type2.isAssignableFrom(type1)) {
138                             return -1;
139                         }
140                     }
141                     // hmmm... same view type, same attributes, same parameter types... ?
142                     return 0;
143                 }
144             };
145 
SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store)146     private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store) {
147         mClassAnalyzer = modelAnalyzer;
148         mStore = store;
149         for (HashMap<AccessorKey, InverseDescription> adapter : mStore.inverseAdapters.values()) {
150             for (InverseDescription inverseDescription : adapter.values()) {
151                 mInverseEventAttributes.add(inverseDescription.event);
152             }
153         }
154         for (HashMap<String, InverseDescription> method : mStore.inverseMethods.values()) {
155             for (InverseDescription inverseDescription : method.values()) {
156                 mInverseEventAttributes.add(inverseDescription.event);
157             }
158         }
159     }
160 
get(ModelAnalyzer modelAnalyzer)161     public static SetterStore get(ModelAnalyzer modelAnalyzer) {
162         if (sStore == null) {
163             sStore = load(modelAnalyzer);
164         }
165         return sStore;
166     }
167 
load(ModelAnalyzer modelAnalyzer)168     private static SetterStore load(ModelAnalyzer modelAnalyzer) {
169         IntermediateV2 store = new IntermediateV2();
170         List<Intermediate> previousStores = GenerationalClassUtil
171                 .loadObjects(GenerationalClassUtil.ExtensionFilter.SETTER_STORE);
172         for (Intermediate intermediate : previousStores) {
173             merge(store, intermediate);
174         }
175         return new SetterStore(modelAnalyzer, store);
176     }
177 
addRenamedMethod(String attribute, String declaringClass, String method, TypeElement declaredOn)178     public void addRenamedMethod(String attribute, String declaringClass, String method,
179             TypeElement declaredOn) {
180         attribute = stripNamespace(attribute);
181         HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
182         if (renamed == null) {
183             renamed = new HashMap<String, MethodDescription>();
184             mStore.renamedMethods.put(attribute, renamed);
185         }
186         MethodDescription methodDescription = new MethodDescription(
187                 declaredOn.getQualifiedName().toString(), method);
188         L.d("STORE addmethod desc %s", methodDescription);
189         renamed.put(declaringClass, methodDescription);
190     }
191 
addInverseMethod(String attribute, String event, String declaringClass, String method, TypeElement declaredOn)192     public void addInverseMethod(String attribute, String event, String declaringClass,
193             String method, TypeElement declaredOn) {
194         attribute = stripNamespace(attribute);
195         event = stripNamespace(event);
196         HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
197         if (inverseMethods == null) {
198             inverseMethods = new HashMap<String, InverseDescription>();
199             mStore.inverseMethods.put(attribute, inverseMethods);
200         }
201         InverseDescription methodDescription = new InverseDescription(
202                 declaredOn.getQualifiedName().toString(), method, event);
203         L.d("STORE addInverseMethod desc %s", methodDescription);
204         inverseMethods.put(declaringClass, methodDescription);
205     }
206 
addBindingAdapter(ProcessingEnvironment processingEnv, String attribute, ExecutableElement bindingMethod, boolean takesComponent)207     public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute,
208             ExecutableElement bindingMethod, boolean takesComponent) {
209         attribute = stripNamespace(attribute);
210         L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod);
211         HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
212 
213         if (adapters == null) {
214             adapters = new HashMap<AccessorKey, MethodDescription>();
215             mStore.adapterMethods.put(attribute, adapters);
216         }
217         List<? extends VariableElement> parameters = bindingMethod.getParameters();
218         final int viewIndex = takesComponent ? 1 : 0;
219         TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
220         String view = getQualifiedName(viewType);
221         TypeMirror parameterType = eraseType(processingEnv, parameters.get(viewIndex + 1).asType());
222         String value = getQualifiedName(parameterType);
223 
224         AccessorKey key = new AccessorKey(view, value);
225         if (adapters.containsKey(key)) {
226             throw new IllegalArgumentException("Already exists!");
227         }
228 
229         adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent));
230     }
231 
addInverseAdapter(ProcessingEnvironment processingEnv, String attribute, String event, ExecutableElement bindingMethod, boolean takesComponent)232     public void addInverseAdapter(ProcessingEnvironment processingEnv, String attribute,
233             String event, ExecutableElement bindingMethod, boolean takesComponent) {
234         attribute = stripNamespace(attribute);
235         event = stripNamespace(event);
236         L.d("STORE addInverseAdapter %s %s", attribute, bindingMethod);
237         HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
238 
239         if (adapters == null) {
240             adapters = new HashMap<AccessorKey, InverseDescription>();
241             mStore.inverseAdapters.put(attribute, adapters);
242         }
243         List<? extends VariableElement> parameters = bindingMethod.getParameters();
244         final int viewIndex = takesComponent ? 1 : 0;
245         TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
246         String view = getQualifiedName(viewType);
247         TypeMirror returnType = eraseType(processingEnv, bindingMethod.getReturnType());
248         String value = getQualifiedName(returnType);
249 
250         AccessorKey key = new AccessorKey(view, value);
251         if (adapters.containsKey(key)) {
252             throw new IllegalArgumentException("Already exists!");
253         }
254 
255         adapters.put(key, new InverseDescription(bindingMethod, event, takesComponent));
256     }
257 
eraseType(ProcessingEnvironment processingEnv, TypeMirror typeMirror)258     private static TypeMirror eraseType(ProcessingEnvironment processingEnv,
259             TypeMirror typeMirror) {
260         if (hasTypeVar(typeMirror)) {
261             return processingEnv.getTypeUtils().erasure(typeMirror);
262         } else {
263             return typeMirror;
264         }
265     }
266 
eraseType(ModelClass modelClass)267     private static ModelClass eraseType(ModelClass modelClass) {
268         if (hasTypeVar(modelClass)) {
269             return modelClass.erasure();
270         } else {
271             return modelClass;
272         }
273     }
274 
hasTypeVar(TypeMirror typeMirror)275     private static boolean hasTypeVar(TypeMirror typeMirror) {
276         TypeKind kind = typeMirror.getKind();
277         if (kind == TypeKind.TYPEVAR) {
278             return true;
279         } else if (kind == TypeKind.ARRAY) {
280             return hasTypeVar(((ArrayType) typeMirror).getComponentType());
281         } else if (kind == TypeKind.DECLARED) {
282             DeclaredType declaredType = (DeclaredType) typeMirror;
283             List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
284             if (typeArguments == null || typeArguments.isEmpty()) {
285                 return false;
286             }
287             for (TypeMirror arg : typeArguments) {
288                 if (hasTypeVar(arg)) {
289                     return true;
290                 }
291             }
292             return false;
293         } else {
294             return false;
295         }
296     }
297 
hasTypeVar(ModelClass type)298     private static boolean hasTypeVar(ModelClass type) {
299         if (type.isTypeVar()) {
300             return true;
301         } else if (type.isArray()) {
302             return hasTypeVar(type.getComponentType());
303         } else {
304             List<ModelClass> typeArguments = type.getTypeArguments();
305             if (typeArguments == null) {
306                 return false;
307             }
308             for (ModelClass arg : typeArguments) {
309                 if (hasTypeVar(arg)) {
310                     return true;
311                 }
312             }
313             return false;
314         }
315     }
316 
addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes, ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll)317     public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes,
318             ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll) {
319         L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod);
320         MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod,
321                 attributes, takesComponent, requireAll);
322         MethodDescription methodDescription = new MethodDescription(bindingMethod,
323                 attributes.length, takesComponent);
324         mStore.multiValueAdapters.put(key, methodDescription);
325     }
326 
stripAttributes(String[] attributes)327     private static String[] stripAttributes(String[] attributes) {
328         String[] strippedAttributes = new String[attributes.length];
329         for (int i = 0; i < attributes.length; i++) {
330             if (attributes[i] != null) {
331                 strippedAttributes[i] = stripNamespace(attributes[i]);
332             }
333         }
334         return strippedAttributes;
335     }
336 
addUntaggableTypes(String[] typeNames, TypeElement declaredOn)337     public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) {
338         L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn);
339         String declaredType = declaredOn.getQualifiedName().toString();
340         for (String type : typeNames) {
341             mStore.untaggableTypes.put(type, declaredType);
342         }
343     }
344 
getQualifiedName(TypeMirror type)345     private static String getQualifiedName(TypeMirror type) {
346         final TypeKind kind = type.getKind();
347         if (kind == TypeKind.ARRAY) {
348             return getQualifiedName(((ArrayType) type).getComponentType()) + "[]";
349         } else if (kind == TypeKind.DECLARED && isIncompleteType(type)) {
350             DeclaredType declaredType = (DeclaredType) type;
351             return declaredType.asElement().toString();
352         } else {
353             return type.toString();
354         }
355     }
356 
isIncompleteType(TypeMirror type)357     private static boolean isIncompleteType(TypeMirror type) {
358         final TypeKind kind = type.getKind();
359         if (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD) {
360             return true;
361         } else if (kind == TypeKind.DECLARED) {
362             DeclaredType declaredType = (DeclaredType) type;
363             List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments();
364             if (typeArgs == null) {
365                 return false;
366             }
367             for (TypeMirror arg : typeArgs) {
368                 if (isIncompleteType(arg)) {
369                     return true;
370                 }
371             }
372         }
373         return false;
374     }
375 
addConversionMethod(ExecutableElement conversionMethod)376     public void addConversionMethod(ExecutableElement conversionMethod) {
377         L.d("STORE addConversionMethod %s", conversionMethod);
378         List<? extends VariableElement> parameters = conversionMethod.getParameters();
379         String fromType = getQualifiedName(parameters.get(0).asType());
380         String toType = getQualifiedName(conversionMethod.getReturnType());
381         MethodDescription methodDescription = new MethodDescription(conversionMethod, 1, false);
382         HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType);
383         if (convertTo == null) {
384             convertTo = new HashMap<String, MethodDescription>();
385             mStore.conversionMethods.put(fromType, convertTo);
386         }
387         convertTo.put(toType, methodDescription);
388     }
389 
clear(Set<String> classes)390     public void clear(Set<String> classes) {
391         ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>();
392         for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) {
393             for (AccessorKey key : adapters.keySet()) {
394                 MethodDescription description = adapters.get(key);
395                 if (classes.contains(description.type)) {
396                     removedAccessorKeys.add(key);
397                 }
398             }
399             removeFromMap(adapters, removedAccessorKeys);
400         }
401 
402         ArrayList<String> removedRenamed = new ArrayList<String>();
403         for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) {
404             for (String key : renamed.keySet()) {
405                 if (classes.contains(renamed.get(key).type)) {
406                     removedRenamed.add(key);
407                 }
408             }
409             removeFromMap(renamed, removedRenamed);
410         }
411 
412         ArrayList<String> removedConversions = new ArrayList<String>();
413         for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) {
414             for (String toType : convertTos.keySet()) {
415                 MethodDescription methodDescription = convertTos.get(toType);
416                 if (classes.contains(methodDescription.type)) {
417                     removedConversions.add(toType);
418                 }
419             }
420             removeFromMap(convertTos, removedConversions);
421         }
422 
423         ArrayList<String> removedUntaggable = new ArrayList<String>();
424         for (String typeName : mStore.untaggableTypes.keySet()) {
425             if (classes.contains(mStore.untaggableTypes.get(typeName))) {
426                 removedUntaggable.add(typeName);
427             }
428         }
429         removeFromMap(mStore.untaggableTypes, removedUntaggable);
430     }
431 
removeFromMap(Map<K, V> map, List<K> keys)432     private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) {
433         for (K key : keys) {
434             map.remove(key);
435         }
436         keys.clear();
437     }
438 
write(String projectPackage, ProcessingEnvironment processingEnvironment)439     public void write(String projectPackage, ProcessingEnvironment processingEnvironment)
440             throws IOException {
441         GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
442                 projectPackage, projectPackage +
443                         GenerationalClassUtil.ExtensionFilter.SETTER_STORE.getExtension(), mStore);
444     }
445 
stripNamespace(String attribute)446     private static String stripNamespace(String attribute) {
447         if (!attribute.startsWith("android:")) {
448             int colon = attribute.indexOf(':');
449             if (colon >= 0) {
450                 attribute = attribute.substring(colon + 1);
451             }
452         }
453         return attribute;
454     }
455 
isTwoWayEventAttribute(String attribute)456     public boolean isTwoWayEventAttribute(String attribute) {
457         attribute = stripNamespace(attribute);
458         return mInverseEventAttributes.contains(attribute);
459     }
getMultiAttributeSetterCalls(String[] attributes, ModelClass viewType, ModelClass[] valueType)460     public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes,
461             ModelClass viewType, ModelClass[] valueType) {
462         attributes = stripAttributes(attributes);
463         final ArrayList<MultiAttributeSetter> calls = new ArrayList<MultiAttributeSetter>();
464         if (viewType != null && viewType.isGeneric()) {
465             List<ModelClass> viewGenerics = viewType.getTypeArguments();
466             for (int i = 0; i < valueType.length; i++) {
467                 valueType[i] = eraseType(valueType[i], viewGenerics);
468             }
469             viewType = viewType.erasure();
470         }
471         ArrayList<MultiAttributeSetter> matching = getMatchingMultiAttributeSetters(attributes,
472                 viewType, valueType);
473         Collections.sort(matching, COMPARE_MULTI_ATTRIBUTE_SETTERS);
474         while (!matching.isEmpty()) {
475             MultiAttributeSetter bestMatch = matching.get(0);
476             calls.add(bestMatch);
477             removeConsumedAttributes(matching, bestMatch.attributes);
478         }
479         return calls;
480     }
481 
simpleName(String className)482     private static String simpleName(String className) {
483         int dotIndex = className.lastIndexOf('.');
484         if (dotIndex < 0) {
485             return className;
486         } else {
487             return className.substring(dotIndex + 1);
488         }
489     }
490 
getComponentBindingAdapters()491     public Map<String, List<String>> getComponentBindingAdapters() {
492         ensureInstanceAdapters();
493         return mInstanceAdapters;
494     }
495 
getBindingAdapterCall(String className)496     private String getBindingAdapterCall(String className) {
497         ensureInstanceAdapters();
498         final String simpleName = simpleName(className);
499         List<String> adapters = mInstanceAdapters.get(simpleName);
500         if (adapters.size() == 1) {
501             return "get" + simpleName + "()";
502         } else {
503             int index = adapters.indexOf(className) + 1;
504             return "get" + simpleName + index + "()";
505         }
506     }
507 
ensureInstanceAdapters()508     private void ensureInstanceAdapters() {
509         if (mInstanceAdapters == null) {
510             HashSet<String> adapters = new HashSet<String>();
511             for (HashMap<AccessorKey, MethodDescription> methods : mStore.adapterMethods.values()) {
512                 for (MethodDescription method : methods.values()) {
513                     if (!method.isStatic) {
514                         adapters.add(method.type);
515                     }
516                 }
517             }
518             for (MethodDescription method : mStore.multiValueAdapters.values()) {
519                 if (!method.isStatic) {
520                     adapters.add(method.type);
521                 }
522             }
523             for (Map<AccessorKey, InverseDescription> methods : mStore.inverseAdapters.values()) {
524                 for (InverseDescription method : methods.values()) {
525                     if (!method.isStatic) {
526                         adapters.add(method.type);
527                     }
528                 }
529             }
530             mInstanceAdapters = new HashMap<String, List<String>>();
531             for (String adapter : adapters) {
532                 final String simpleName = simpleName(adapter);
533                 List<String> list = mInstanceAdapters.get(simpleName);
534                 if (list == null) {
535                     list = new ArrayList<String>();
536                     mInstanceAdapters.put(simpleName, list);
537                 }
538                 list.add(adapter);
539             }
540             for (List<String> list : mInstanceAdapters.values()) {
541                 if (list.size() > 1) {
542                     Collections.sort(list);
543                 }
544             }
545         }
546     }
547 
548     // Removes all MultiAttributeSetters that require any of the values in attributes
removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching, String[] attributes)549     private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching,
550             String[] attributes) {
551         for (int i = matching.size() - 1; i >= 0; i--) {
552             final MultiAttributeSetter setter = matching.get(i);
553             boolean found = false;
554             for (String attribute : attributes) {
555                 if (isInArray(attribute, setter.attributes)) {
556                     found = true;
557                     break;
558                 }
559             }
560             if (found) {
561                 matching.remove(i);
562             }
563         }
564     }
565 
566     // Linear search through the String array for a specific value.
isInArray(String str, String[] array)567     private static boolean isInArray(String str, String[] array) {
568         for (String value : array) {
569             if (value.equals(str)) {
570                 return true;
571             }
572         }
573         return false;
574     }
575 
getMatchingMultiAttributeSetters(String[] attributes, ModelClass viewType, ModelClass[] valueType)576     private ArrayList<MultiAttributeSetter> getMatchingMultiAttributeSetters(String[] attributes,
577             ModelClass viewType, ModelClass[] valueType) {
578         final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>();
579         for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) {
580             if (adapter.requireAll && adapter.attributes.length > attributes.length) {
581                 continue;
582             }
583             ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null);
584             if (viewClass.isGeneric()) {
585                 viewClass = viewClass.erasure();
586             }
587             if (!viewClass.isAssignableFrom(viewType)) {
588                 continue;
589             }
590             final MethodDescription method = mStore.multiValueAdapters.get(adapter);
591             final MultiAttributeSetter setter = createMultiAttributeSetter(method, attributes,
592                     valueType, adapter);
593             if (setter != null) {
594                 setters.add(setter);
595             }
596         }
597         return setters;
598     }
599 
createMultiAttributeSetter(MethodDescription method, String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter)600     private MultiAttributeSetter createMultiAttributeSetter(MethodDescription method,
601             String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter) {
602         int matchingAttributes = 0;
603         String[] casts = new String[adapter.attributes.length];
604         MethodDescription[] conversions = new MethodDescription[adapter.attributes.length];
605         boolean[] supplied = new boolean[adapter.attributes.length];
606 
607         for (int i = 0; i < allAttributes.length; i++) {
608             Integer index = adapter.attributeIndices.get(allAttributes[i]);
609             if (index != null) {
610                 supplied[index] = true;
611                 matchingAttributes++;
612                 final String parameterTypeStr = adapter.parameterTypes[index];
613                 final ModelClass parameterType = eraseType(
614                         mClassAnalyzer.findClass(parameterTypeStr, null));
615                 final ModelClass attributeType = attributeValues[i];
616                 if (!parameterType.isAssignableFrom(attributeType)) {
617                     if (ModelMethod.isBoxingConversion(parameterType, attributeType)) {
618                         // automatic boxing is ok
619                         continue;
620                     } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) {
621                         // implicit conversion is ok
622                         continue;
623                     }
624                     // Look for a converter
625                     conversions[index] = getConversionMethod(attributeType, parameterType, null);
626                     if (conversions[index] == null) {
627                         if (attributeType.isObject()) {
628                             // Cast is allowed also
629                             casts[index] = parameterTypeStr;
630                         } else {
631                             // Parameter type mismatch
632                             return null;
633                         }
634                     }
635                 }
636             }
637         }
638 
639         if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) ||
640                 matchingAttributes == 0) {
641             return null;
642         } else {
643             return new MultiAttributeSetter(adapter, supplied, method, conversions, casts);
644         }
645     }
646 
getSetterCall(String attribute, ModelClass viewType, ModelClass valueType, Map<String, String> imports)647     public SetterCall getSetterCall(String attribute, ModelClass viewType,
648             ModelClass valueType, Map<String, String> imports) {
649         attribute = stripNamespace(attribute);
650         SetterCall setterCall = null;
651         MethodDescription conversionMethod = null;
652         if (viewType != null) {
653             viewType = viewType.erasure();
654             HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
655             ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports);
656             ModelClass bestViewType = null;
657             ModelClass bestValueType = null;
658             if (bestSetterMethod != null) {
659                 bestViewType = bestSetterMethod.getDeclaringClass();
660                 bestValueType = bestSetterMethod.getParameterTypes()[0];
661                 setterCall = new ModelMethodSetter(bestSetterMethod);
662             }
663 
664             if (adapters != null) {
665                 for (AccessorKey key : adapters.keySet()) {
666                     try {
667                         ModelClass adapterViewType = mClassAnalyzer
668                                 .findClass(key.viewType, imports).erasure();
669                         if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) {
670                             try {
671                                 L.d("setter parameter type is %s", key.valueType);
672                                 final ModelClass adapterValueType = eraseType(mClassAnalyzer
673                                         .findClass(key.valueType, imports));
674                                 L.d("setter %s takes type %s, compared to %s",
675                                         adapters.get(key).method, adapterValueType.toJavaCode(),
676                                         valueType.toJavaCode());
677                                 boolean isBetterView = bestViewType == null ||
678                                         bestViewType.isAssignableFrom(adapterViewType);
679                                 if (isBetterParameter(valueType, adapterValueType, bestValueType,
680                                         isBetterView, imports)) {
681                                     bestViewType = adapterViewType;
682                                     bestValueType = adapterValueType;
683                                     MethodDescription adapter = adapters.get(key);
684                                     setterCall = new AdapterSetter(adapter, adapterValueType);
685                                 }
686 
687                             } catch (Exception e) {
688                                 L.e(e, "Unknown class: %s", key.valueType);
689                             }
690                         }
691                     } catch (Exception e) {
692                         L.e(e, "Unknown class: %s", key.viewType);
693                     }
694                 }
695             }
696 
697             conversionMethod = getConversionMethod(valueType, bestValueType, imports);
698             if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) {
699                 setterCall.setCast(bestValueType);
700             }
701         }
702         if (setterCall == null) {
703             if (viewType != null && !viewType.isViewDataBinding()) {
704                 return null; // no setter found!!
705             }
706             setterCall = new DummySetter(getDefaultSetter(attribute));
707         }
708         setterCall.setConverter(conversionMethod);
709         return setterCall;
710     }
711 
getGetterCall(String attribute, ModelClass viewType, ModelClass valueType, Map<String, String> imports)712     public BindingGetterCall getGetterCall(String attribute, ModelClass viewType,
713             ModelClass valueType, Map<String, String> imports) {
714         if (viewType == null) {
715             return null;
716         } else if (viewType.isViewDataBinding()) {
717             return new ViewDataBindingGetterCall(viewType, attribute);
718         }
719 
720         attribute = stripNamespace(attribute);
721         viewType = viewType.erasure();
722 
723         InverseMethod bestMethod = getBestGetter(viewType, valueType, attribute, imports);
724         HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
725         if (adapters != null) {
726             for (AccessorKey key : adapters.keySet()) {
727                 try {
728                     ModelClass adapterViewType = mClassAnalyzer
729                             .findClass(key.viewType, imports).erasure();
730                     if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) {
731                         try {
732                             L.d("getter return type is %s", key.valueType);
733                             final ModelClass adapterValueType = eraseType(mClassAnalyzer
734                                     .findClass(key.valueType, imports));
735                             L.d("getter %s returns type %s, compared to %s",
736                                     adapters.get(key).method, adapterValueType.toJavaCode(),
737                                     valueType);
738                             boolean isBetterView = bestMethod.viewType == null ||
739                                     bestMethod.viewType.isAssignableFrom(adapterViewType);
740                             if (valueType == null ||
741                                     isBetterParameter(adapterValueType, valueType,
742                                             bestMethod.returnType, isBetterView, imports)) {
743                                 bestMethod.viewType = adapterViewType;
744                                 bestMethod.returnType = adapterValueType;
745                                 InverseDescription inverseDescription = adapters.get(key);
746                                 ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
747                                         InverseBindingListener.class);
748                                 BindingSetterCall eventCall = getSetterCall(
749                                         inverseDescription.event, viewType, listenerType, imports);
750                                 if (eventCall == null) {
751                                     List<MultiAttributeSetter> setters =
752                                             getMultiAttributeSetterCalls(
753                                                     new String[]{inverseDescription.event},
754                                                     viewType, new ModelClass[] {listenerType});
755                                     if (setters.size() != 1) {
756                                         L.e("Could not find event '%s' on View type '%s'",
757                                                 inverseDescription.event,
758                                                 viewType.getCanonicalName());
759                                     } else {
760                                         bestMethod.call = new AdapterGetter(inverseDescription,
761                                                 setters.get(0), key.valueType);
762                                     }
763                                 } else {
764                                     bestMethod.call = new AdapterGetter(inverseDescription,
765                                             eventCall, key.valueType);
766                                 }
767                             }
768 
769                         } catch (Exception e) {
770                             L.e(e, "Unknown class: %s", key.valueType);
771                         }
772                     }
773                 } catch (Exception e) {
774                     L.e(e, "Unknown class: %s", key.viewType);
775                 }
776             }
777         }
778 
779         return bestMethod.call;
780     }
781 
isUntaggable(String viewType)782     public boolean isUntaggable(String viewType) {
783         return mStore.untaggableTypes.containsKey(viewType);
784     }
785 
getBestSetter(ModelClass viewType, ModelClass argumentType, String attribute, Map<String, String> imports)786     private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType,
787             String attribute, Map<String, String> imports) {
788         if (viewType.isGeneric()) {
789             argumentType = eraseType(argumentType, viewType.getTypeArguments());
790             viewType = viewType.erasure();
791         }
792         List<String> setterCandidates = new ArrayList<String>();
793         HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
794         if (renamed != null) {
795             for (String className : renamed.keySet()) {
796                 try {
797                     ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports);
798                     if (renamedViewType.erasure().isAssignableFrom(viewType)) {
799                         setterCandidates.add(renamed.get(className).method);
800                         break;
801                     }
802                 } catch (Exception e) {
803                     //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
804                 }
805             }
806         }
807         setterCandidates.add(getDefaultSetter(attribute));
808         setterCandidates.add(trimAttributeNamespace(attribute));
809 
810         ModelMethod bestMethod = null;
811         ModelClass bestParameterType = null;
812         List<ModelClass> args = new ArrayList<ModelClass>();
813         args.add(argumentType);
814         for (String name : setterCandidates) {
815             ModelMethod[] methods = viewType.getMethods(name, 1);
816 
817             for (ModelMethod method : methods) {
818                 ModelClass[] parameterTypes = method.getParameterTypes();
819                 ModelClass param = parameterTypes[0];
820                 if (method.isVoid() &&
821                         isBetterParameter(argumentType, param, bestParameterType, true, imports)) {
822                     bestParameterType = param;
823                     bestMethod = method;
824                 }
825             }
826         }
827         return bestMethod;
828     }
829 
getBestGetter(ModelClass viewType, ModelClass valueType, String attribute, Map<String, String> imports)830     private InverseMethod getBestGetter(ModelClass viewType, ModelClass valueType,
831             String attribute, Map<String, String> imports) {
832         if (viewType.isGeneric()) {
833             if (valueType != null) {
834                 valueType = eraseType(valueType, viewType.getTypeArguments());
835             }
836             viewType = viewType.erasure();
837         }
838         ModelClass bestReturnType = null;
839         InverseDescription bestDescription = null;
840         ModelClass bestViewType = null;
841         ModelMethod bestMethod = null;
842 
843         HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
844         if (inverseMethods != null) {
845             for (String className : inverseMethods.keySet()) {
846                 try {
847                     ModelClass methodViewType = mClassAnalyzer.findClass(className, imports);
848                     if (methodViewType.erasure().isAssignableFrom(viewType)) {
849                         boolean isBetterViewType = bestViewType == null ||
850                                 bestViewType.isAssignableFrom(methodViewType);
851                         final InverseDescription inverseDescription = inverseMethods.get(className);
852                         final String name =  inverseDescription.method.isEmpty() ?
853                                 trimAttributeNamespace(attribute) : inverseDescription.method;
854                         ModelMethod method = methodViewType.findInstanceGetter(name);
855                         ModelClass returnType = method.getReturnType(null); // no parameters
856                         if (valueType == null || bestReturnType == null ||
857                                 isBetterParameter(returnType, valueType, bestReturnType,
858                                         isBetterViewType, imports)) {
859                             bestDescription = inverseDescription;
860                             bestReturnType = returnType;
861                             bestViewType = methodViewType;
862                             bestMethod = method;
863                         }
864                     }
865                 } catch (Exception e) {
866                     //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
867                 }
868             }
869         }
870 
871         BindingGetterCall call = null;
872         if (bestDescription != null) {
873             final ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
874                     InverseBindingListener.class);
875             SetterCall eventSetter = getSetterCall(bestDescription.event, viewType,
876                     listenerType, imports);
877             if (eventSetter == null) {
878                 List<MultiAttributeSetter> setters = getMultiAttributeSetterCalls(
879                         new String[] {bestDescription.event}, viewType,
880                         new ModelClass[] {listenerType});
881                 if (setters.size() != 1) {
882                     L.e("Could not find event '%s' on View type '%s'", bestDescription.event,
883                             viewType.getCanonicalName());
884                     bestViewType = null;
885                     bestReturnType = null;
886                 } else {
887                     call = new ViewGetterCall(bestDescription, bestMethod, setters.get(0));
888                 }
889             } else {
890                 call = new ViewGetterCall(bestDescription, bestMethod, eventSetter);
891             }
892         }
893         return new InverseMethod(call, bestReturnType, bestViewType);
894     }
895 
eraseType(ModelClass type, List<ModelClass> typeParameters)896     private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) {
897         List<ModelClass> typeArguments = type.getTypeArguments();
898         if (typeArguments == null || typeParameters == null) {
899             return type;
900         }
901         for (ModelClass arg : typeArguments) {
902             if (typeParameters.contains(arg)) {
903                 return type.erasure();
904             }
905         }
906         return type;
907     }
908 
trimAttributeNamespace(String attribute)909     private static String trimAttributeNamespace(String attribute) {
910         final int colonIndex = attribute.indexOf(':');
911         return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1);
912     }
913 
getDefaultSetter(String attribute)914     private static String getDefaultSetter(String attribute) {
915         return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute));
916     }
917 
isBetterParameter(ModelClass argument, ModelClass parameter, ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports)918     private boolean isBetterParameter(ModelClass argument, ModelClass parameter,
919             ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) {
920         // Right view type. Check the value
921         if (!isBetterViewTypeMatch && oldParameter.equals(argument)) {
922             return false;
923         } else if (argument.equals(parameter)) {
924             // Exact match
925             return true;
926         } else if (!isBetterViewTypeMatch &&
927                 ModelMethod.isBoxingConversion(oldParameter, argument)) {
928             return false;
929         } else if (ModelMethod.isBoxingConversion(parameter, argument)) {
930             // Boxing/unboxing is second best
931             return true;
932         } else {
933             int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter);
934             if (ModelMethod.isImplicitConversion(argument, parameter)) {
935                 // Better implicit conversion
936                 int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter);
937                 return oldConversionLevel < 0 || conversionLevel < oldConversionLevel;
938             } else if (oldConversionLevel >= 0) {
939                 return false;
940             } else if (parameter.isAssignableFrom(argument)) {
941                 // Right type, see if it is better than the current best match.
942                 if (oldParameter == null) {
943                     return true;
944                 } else {
945                     return oldParameter.isAssignableFrom(parameter);
946                 }
947             } else {
948                 MethodDescription conversionMethod = getConversionMethod(argument, parameter,
949                         imports);
950                 if (conversionMethod != null) {
951                     return true;
952                 }
953                 if (getConversionMethod(argument, oldParameter, imports) != null) {
954                     return false;
955                 }
956                 return argument.isObject() && !parameter.isPrimitive();
957             }
958         }
959     }
960 
getConversionMethod(ModelClass from, ModelClass to, Map<String, String> imports)961     private MethodDescription getConversionMethod(ModelClass from, ModelClass to,
962             Map<String, String> imports) {
963         if (from != null && to != null) {
964             if (to.isObject()) {
965                 return null;
966             }
967             for (String fromClassName : mStore.conversionMethods.keySet()) {
968                 try {
969                     ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports);
970                     if (canUseForConversion(from, convertFrom)) {
971                         HashMap<String, MethodDescription> conversion =
972                                 mStore.conversionMethods.get(fromClassName);
973                         for (String toClassName : conversion.keySet()) {
974                             try {
975                                 ModelClass convertTo = mClassAnalyzer.findClass(toClassName,
976                                         imports);
977                                 if (canUseForConversion(convertTo, to)) {
978                                     return conversion.get(toClassName);
979                                 }
980                             } catch (Exception e) {
981                                 L.d(e, "Unknown class: %s", toClassName);
982                             }
983                         }
984                     }
985                 } catch (Exception e) {
986                     L.d(e, "Unknown class: %s", fromClassName);
987                 }
988             }
989         }
990         return null;
991     }
992 
canUseForConversion(ModelClass from, ModelClass to)993     private boolean canUseForConversion(ModelClass from, ModelClass to) {
994         if (from.isIncomplete() || to.isIncomplete()) {
995             from = from.erasure();
996             to = to.erasure();
997         }
998         return from.equals(to) || ModelMethod.isBoxingConversion(from, to) ||
999                 to.isAssignableFrom(from);
1000     }
1001 
merge(IntermediateV2 store, Intermediate dumpStore)1002     private static void merge(IntermediateV2 store, Intermediate dumpStore) {
1003         IntermediateV2 intermediateV2 = (IntermediateV2) dumpStore.upgrade();
1004         merge(store.adapterMethods, intermediateV2.adapterMethods);
1005         merge(store.renamedMethods, intermediateV2.renamedMethods);
1006         merge(store.conversionMethods, intermediateV2.conversionMethods);
1007         store.multiValueAdapters.putAll(intermediateV2.multiValueAdapters);
1008         store.untaggableTypes.putAll(intermediateV2.untaggableTypes);
1009         merge(store.inverseAdapters, intermediateV2.inverseAdapters);
1010         merge(store.inverseMethods, intermediateV2.inverseMethods);
1011     }
1012 
merge(HashMap<K, HashMap<V, D>> first, HashMap<K, HashMap<V, D>> second)1013     private static <K, V, D> void merge(HashMap<K, HashMap<V, D>> first,
1014             HashMap<K, HashMap<V, D>> second) {
1015         for (K key : second.keySet()) {
1016             HashMap<V, D> firstVals = first.get(key);
1017             HashMap<V, D> secondVals = second.get(key);
1018             if (firstVals == null) {
1019                 first.put(key, secondVals);
1020             } else {
1021                 for (V key2 : secondVals.keySet()) {
1022                     if (!firstVals.containsKey(key2)) {
1023                         firstVals.put(key2, secondVals.get(key2));
1024                     }
1025                 }
1026             }
1027         }
1028     }
1029 
createAdapterCall(MethodDescription adapter, String componentExpression, String viewExpression, String... args)1030     private static String createAdapterCall(MethodDescription adapter,
1031             String componentExpression, String viewExpression, String... args) {
1032         StringBuilder sb = new StringBuilder();
1033 
1034         if (adapter.isStatic) {
1035             sb.append(adapter.type);
1036         } else {
1037             final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
1038             final String binderCall =  setterStore.getBindingAdapterCall(adapter.type);
1039             sb.append(componentExpression).append('.').append(binderCall);
1040         }
1041         sb.append('.').append(adapter.method).append('(');
1042         if (adapter.componentClass != null) {
1043             if (!"DataBindingComponent".equals(adapter.componentClass)) {
1044                 sb.append('(').append(adapter.componentClass).append(") ");
1045             }
1046             sb.append(componentExpression).append(", ");
1047         }
1048         sb.append(viewExpression);
1049         for (String arg: args) {
1050             sb.append(", ").append(arg);
1051         }
1052         sb.append(')');
1053         return sb.toString();
1054     }
1055 
1056     private static class MultiValueAdapterKey implements Serializable {
1057         private static final long serialVersionUID = 1;
1058 
1059         public final String viewType;
1060 
1061         public final String[] attributes;
1062 
1063         public final String[] parameterTypes;
1064 
1065         public final boolean requireAll;
1066 
1067         public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>();
1068 
MultiValueAdapterKey(ProcessingEnvironment processingEnv, ExecutableElement method, String[] attributes, boolean takesComponent, boolean requireAll)1069         public MultiValueAdapterKey(ProcessingEnvironment processingEnv,
1070                 ExecutableElement method, String[] attributes, boolean takesComponent,
1071                 boolean requireAll) {
1072             this.attributes = stripAttributes(attributes);
1073             this.requireAll = requireAll;
1074             List<? extends VariableElement> parameters = method.getParameters();
1075             final int argStart = 1 + (takesComponent ? 1 : 0);
1076             this.viewType = getQualifiedName(eraseType(processingEnv,
1077                     parameters.get(argStart - 1).asType()));
1078             this.parameterTypes = new String[parameters.size() - argStart];
1079             for (int i = 0; i < attributes.length; i++) {
1080                 TypeMirror typeMirror = eraseType(processingEnv,
1081                         parameters.get(i + argStart).asType());
1082                 this.parameterTypes[i] = getQualifiedName(typeMirror);
1083                 attributeIndices.put(this.attributes[i], i);
1084             }
1085         }
1086 
1087         @Override
equals(Object obj)1088         public boolean equals(Object obj) {
1089             if (!(obj instanceof MultiValueAdapterKey)) {
1090                 return false;
1091             }
1092             final MultiValueAdapterKey that = (MultiValueAdapterKey) obj;
1093             if (!this.viewType.equals(that.viewType) ||
1094                     this.attributes.length != that.attributes.length ||
1095                     !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) {
1096                 return false;
1097             }
1098 
1099             for (int i = 0; i < this.attributes.length; i++) {
1100                 final int thatIndex = that.attributeIndices.get(this.attributes[i]);
1101                 final String thisParameter = parameterTypes[i];
1102                 final String thatParameter = that.parameterTypes[thatIndex];
1103                 if (!thisParameter.equals(thatParameter)) {
1104                     return false;
1105                 }
1106             }
1107             return true;
1108         }
1109 
1110         @Override
hashCode()1111         public int hashCode() {
1112             return mergedHashCode(viewType, attributeIndices.keySet());
1113         }
1114     }
1115 
mergedHashCode(Object... objects)1116     private static int mergedHashCode(Object... objects) {
1117         return Arrays.hashCode(objects);
1118     }
1119 
1120     private static class MethodDescription implements Serializable {
1121 
1122         private static final long serialVersionUID = 1;
1123 
1124         public final String type;
1125 
1126         public final String method;
1127 
1128         public final boolean requiresOldValue;
1129 
1130         public final boolean isStatic;
1131 
1132         public final String componentClass;
1133 
MethodDescription(String type, String method)1134         public MethodDescription(String type, String method) {
1135             this.type = type;
1136             this.method = method;
1137             this.requiresOldValue = false;
1138             this.isStatic = true;
1139             this.componentClass = null;
1140             L.d("BINARY created method desc 1 %s %s", type, method );
1141         }
1142 
MethodDescription(ExecutableElement method, int numAttributes, boolean takesComponent)1143         public MethodDescription(ExecutableElement method, int numAttributes,
1144                 boolean takesComponent) {
1145             TypeElement enclosingClass = (TypeElement) method.getEnclosingElement();
1146             this.type = enclosingClass.getQualifiedName().toString();
1147             this.method = method.getSimpleName().toString();
1148             final int argStart = 1 + (takesComponent ? 1 : 0);
1149             this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2;
1150             this.isStatic = method.getModifiers().contains(Modifier.STATIC);
1151             this.componentClass = takesComponent
1152                     ? getQualifiedName(method.getParameters().get(0).asType())
1153                     : null;
1154 
1155             L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method);
1156         }
1157 
1158         @Override
equals(Object obj)1159         public boolean equals(Object obj) {
1160             if (obj instanceof MethodDescription) {
1161                 MethodDescription that = (MethodDescription) obj;
1162                 return that.type.equals(this.type) && that.method.equals(this.method);
1163             } else {
1164                 return false;
1165             }
1166         }
1167 
1168         @Override
hashCode()1169         public int hashCode() {
1170             return mergedHashCode(type, method);
1171         }
1172 
1173         @Override
toString()1174         public String toString() {
1175             return type + "." + method + "()";
1176         }
1177     }
1178 
1179     private static class InverseDescription extends MethodDescription {
1180         private static final long serialVersionUID = 1;
1181 
1182         public final String event;
1183 
InverseDescription(String type, String method, String event)1184         public InverseDescription(String type, String method, String event) {
1185             super(type, method);
1186             this.event = event;
1187         }
1188 
InverseDescription(ExecutableElement method, String event, boolean takesComponent)1189         public InverseDescription(ExecutableElement method, String event, boolean takesComponent) {
1190             super(method, 1, takesComponent);
1191             this.event = event;
1192         }
1193 
1194         @Override
equals(Object obj)1195         public boolean equals(Object obj) {
1196             if (!super.equals(obj) || !(obj instanceof InverseDescription)) {
1197                 return false;
1198             }
1199             return event.equals(((InverseDescription) obj).event);
1200         }
1201 
1202         @Override
hashCode()1203         public int hashCode() {
1204             return mergedHashCode(type, method, event);
1205         }
1206     }
1207 
1208     private static class AccessorKey implements Serializable {
1209 
1210         private static final long serialVersionUID = 1;
1211 
1212         public final String viewType;
1213 
1214         public final String valueType;
1215 
AccessorKey(String viewType, String valueType)1216         public AccessorKey(String viewType, String valueType) {
1217             this.viewType = viewType;
1218             this.valueType = valueType;
1219         }
1220 
1221         @Override
hashCode()1222         public int hashCode() {
1223             return mergedHashCode(viewType, valueType);
1224         }
1225 
1226         @Override
equals(Object obj)1227         public boolean equals(Object obj) {
1228             if (obj instanceof AccessorKey) {
1229                 AccessorKey that = (AccessorKey) obj;
1230                 return viewType.equals(that.valueType) && valueType.equals(that.valueType);
1231             } else {
1232                 return false;
1233             }
1234         }
1235 
1236         @Override
toString()1237         public String toString() {
1238             return "AK(" + viewType + ", " + valueType + ")";
1239         }
1240     }
1241 
1242     private interface Intermediate extends Serializable {
upgrade()1243         Intermediate upgrade();
1244     }
1245 
1246     private static class IntermediateV1 implements Serializable, Intermediate {
1247         private static final long serialVersionUID = 1;
1248         public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods =
1249                 new HashMap<String, HashMap<AccessorKey, MethodDescription>>();
1250         public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods =
1251                 new HashMap<String, HashMap<String, MethodDescription>>();
1252         public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods =
1253                 new HashMap<String, HashMap<String, MethodDescription>>();
1254         public final HashMap<String, String> untaggableTypes = new HashMap<String, String>();
1255         public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters =
1256                 new HashMap<MultiValueAdapterKey, MethodDescription>();
1257 
IntermediateV1()1258         public IntermediateV1() {
1259         }
1260 
1261         @Override
upgrade()1262         public Intermediate upgrade() {
1263             IntermediateV2 v2 = new IntermediateV2();
1264             v2.adapterMethods.putAll(adapterMethods);
1265             v2.renamedMethods.putAll(renamedMethods);
1266             v2.conversionMethods.putAll(conversionMethods);
1267             v2.untaggableTypes.putAll(untaggableTypes);
1268             v2.multiValueAdapters.putAll(multiValueAdapters);
1269             return v2;
1270         }
1271     }
1272 
1273     private static class IntermediateV2 extends IntermediateV1 {
1274         private static final long serialVersionUID = 0xA45C2EB637E35C07L;
1275         public final HashMap<String, HashMap<AccessorKey, InverseDescription>> inverseAdapters =
1276                 new HashMap<String, HashMap<AccessorKey, InverseDescription>>();
1277         public final HashMap<String, HashMap<String, InverseDescription>> inverseMethods =
1278                 new HashMap<String, HashMap<String, InverseDescription>>();
1279 
1280         @Override
upgrade()1281         public Intermediate upgrade() {
1282             return this;
1283         }
1284     }
1285 
1286     public static class DummySetter extends SetterCall {
1287         private String mMethodName;
1288 
DummySetter(String methodName)1289         public DummySetter(String methodName) {
1290             mMethodName = methodName;
1291         }
1292 
1293         @Override
toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1294         public String toJavaInternal(String componentExpression, String viewExpression,
1295                 String valueExpression) {
1296             return viewExpression + "." + mMethodName + "(" + valueExpression + ")";
1297         }
1298 
1299         @Override
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1300         public String toJavaInternal(String componentExpression, String viewExpression,
1301                 String oldValue, String valueExpression) {
1302             return viewExpression + "." + mMethodName + "(" + valueExpression + ")";
1303         }
1304 
1305         @Override
getMinApi()1306         public int getMinApi() {
1307             return 1;
1308         }
1309 
1310         @Override
requiresOldValue()1311         public boolean requiresOldValue() {
1312             return false;
1313         }
1314 
1315         @Override
getParameterTypes()1316         public ModelClass[] getParameterTypes() {
1317             return new ModelClass[] {
1318                     ModelAnalyzer.getInstance().findClass(Object.class)
1319             };
1320         }
1321 
1322         @Override
getBindingAdapterInstanceClass()1323         public String getBindingAdapterInstanceClass() {
1324             return null;
1325         }
1326     }
1327 
1328     public static class AdapterSetter extends SetterCall {
1329         final MethodDescription mAdapter;
1330         final ModelClass mParameterType;
1331 
AdapterSetter(MethodDescription adapter, ModelClass parameterType)1332         public AdapterSetter(MethodDescription adapter, ModelClass parameterType) {
1333             mAdapter = adapter;
1334             mParameterType = parameterType;
1335         }
1336 
1337         @Override
toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1338         public String toJavaInternal(String componentExpression, String viewExpression,
1339                 String valueExpression) {
1340             return createAdapterCall(mAdapter, componentExpression,
1341                     viewExpression, mCastString + valueExpression);
1342         }
1343 
1344         @Override
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1345         protected String toJavaInternal(String componentExpression, String viewExpression,
1346                 String oldValue, String valueExpression) {
1347             return createAdapterCall(mAdapter, componentExpression,
1348                     viewExpression, mCastString + oldValue, mCastString + valueExpression);
1349         }
1350 
1351         @Override
getMinApi()1352         public int getMinApi() {
1353             return 1;
1354         }
1355 
1356         @Override
requiresOldValue()1357         public boolean requiresOldValue() {
1358             return mAdapter.requiresOldValue;
1359         }
1360 
1361         @Override
getParameterTypes()1362         public ModelClass[] getParameterTypes() {
1363             return new ModelClass[] { mParameterType };
1364         }
1365 
1366         @Override
getBindingAdapterInstanceClass()1367         public String getBindingAdapterInstanceClass() {
1368             return mAdapter.isStatic ? null : mAdapter.type;
1369         }
1370     }
1371 
1372     public static class ModelMethodSetter extends SetterCall {
1373         final ModelMethod mModelMethod;
1374 
ModelMethodSetter(ModelMethod modelMethod)1375         public ModelMethodSetter(ModelMethod modelMethod) {
1376             mModelMethod = modelMethod;
1377         }
1378 
1379         @Override
toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1380         public String toJavaInternal(String componentExpression, String viewExpression,
1381                 String valueExpression) {
1382             return viewExpression + "." + mModelMethod.getName() + "(" + mCastString +
1383                     valueExpression + ")";
1384         }
1385 
1386         @Override
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1387         protected String toJavaInternal(String componentExpression, String viewExpression,
1388                 String oldValue, String valueExpression) {
1389             return viewExpression + "." + mModelMethod.getName() + "(" +
1390                     mCastString + oldValue + ", " + mCastString + valueExpression + ")";
1391         }
1392 
1393         @Override
getMinApi()1394         public int getMinApi() {
1395             return mModelMethod.getMinApi();
1396         }
1397 
1398         @Override
requiresOldValue()1399         public boolean requiresOldValue() {
1400             return mModelMethod.getParameterTypes().length == 3;
1401         }
1402 
1403         @Override
getParameterTypes()1404         public ModelClass[] getParameterTypes() {
1405             return new ModelClass[] { mModelMethod.getParameterTypes()[0] };
1406         }
1407 
1408         @Override
getBindingAdapterInstanceClass()1409         public String getBindingAdapterInstanceClass() {
1410             return null;
1411         }
1412     }
1413 
1414     public interface BindingSetterCall {
toJava(String componentExpression, String viewExpression, String... valueExpressions)1415         String toJava(String componentExpression, String viewExpression,
1416                 String... valueExpressions);
1417 
getMinApi()1418         int getMinApi();
1419 
requiresOldValue()1420         boolean requiresOldValue();
1421 
getParameterTypes()1422         ModelClass[] getParameterTypes();
1423 
getBindingAdapterInstanceClass()1424         String getBindingAdapterInstanceClass();
1425     }
1426 
1427     public static abstract class SetterCall implements BindingSetterCall {
1428         private MethodDescription mConverter;
1429         protected String mCastString = "";
1430 
SetterCall()1431         public SetterCall() {
1432         }
1433 
setConverter(MethodDescription converter)1434         public void setConverter(MethodDescription converter) {
1435             mConverter = converter;
1436         }
1437 
toJavaInternal(String componentExpression, String viewExpression, String converted)1438         protected abstract String toJavaInternal(String componentExpression, String viewExpression,
1439                 String converted);
1440 
toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted)1441         protected abstract String toJavaInternal(String componentExpression, String viewExpression,
1442                 String oldValue, String converted);
1443 
1444         @Override
toJava(String componentExpression, String viewExpression, String... valueExpression)1445         public final String toJava(String componentExpression, String viewExpression,
1446                 String... valueExpression) {
1447             Preconditions.check(valueExpression.length == 2, "value expressions size must be 2");
1448             if (requiresOldValue()) {
1449                 return toJavaInternal(componentExpression, viewExpression,
1450                         convertValue(valueExpression[0]), convertValue(valueExpression[1]));
1451             } else {
1452                 return toJavaInternal(componentExpression, viewExpression,
1453                         convertValue(valueExpression[1]));
1454             }
1455         }
1456 
convertValue(String valueExpression)1457         protected String convertValue(String valueExpression) {
1458             return mConverter == null ? valueExpression :
1459                     mConverter.type + "." + mConverter.method + "(" + valueExpression + ")";
1460         }
1461 
getMinApi()1462         abstract public int getMinApi();
1463 
setCast(ModelClass castTo)1464         public void setCast(ModelClass castTo) {
1465             mCastString = "(" + castTo.toJavaCode() + ") ";
1466         }
1467     }
1468 
1469     public static class MultiAttributeSetter implements BindingSetterCall {
1470         public final String[] attributes;
1471         private final MethodDescription mAdapter;
1472         private final MethodDescription[] mConverters;
1473         private final String[] mCasts;
1474         private final MultiValueAdapterKey mKey;
1475         private final boolean[] mSupplied;
1476 
MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, MethodDescription adapter, MethodDescription[] converters, String[] casts)1477         public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied,
1478                 MethodDescription adapter, MethodDescription[] converters, String[] casts) {
1479             Preconditions.check(converters != null &&
1480                     converters.length == key.attributes.length &&
1481                     casts != null && casts.length == key.attributes.length &&
1482                     supplied.length == key.attributes.length,
1483                     "invalid arguments to create multi attr setter");
1484             this.mAdapter = adapter;
1485             this.mConverters = converters;
1486             this.mCasts = casts;
1487             this.mKey = key;
1488             this.mSupplied = supplied;
1489             if (key.requireAll) {
1490                 this.attributes = key.attributes;
1491             } else {
1492                 int numSupplied = 0;
1493                 for (int i = 0; i < mKey.attributes.length; i++) {
1494                     if (supplied[i]) {
1495                         numSupplied++;
1496                     }
1497                 }
1498                 if (numSupplied == key.attributes.length) {
1499                     this.attributes = key.attributes;
1500                 } else {
1501                     this.attributes = new String[numSupplied];
1502                     int attrIndex = 0;
1503                     for (int i = 0; i < key.attributes.length; i++) {
1504                         if (supplied[i]) {
1505                             attributes[attrIndex++] = key.attributes[i];
1506                         }
1507                     }
1508                 }
1509             }
1510         }
1511 
1512         @Override
toJava(String componentExpression, String viewExpression, String[] valueExpressions)1513         public final String toJava(String componentExpression, String viewExpression,
1514                 String[] valueExpressions) {
1515             Preconditions.check(valueExpressions.length == attributes.length * 2,
1516                     "MultiAttributeSetter needs %s items, received %s",
1517                     Arrays.toString(attributes), Arrays.toString(valueExpressions));
1518             final int numAttrs = mKey.attributes.length;
1519             String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)];
1520 
1521             final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs;
1522             int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length;
1523             final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
1524             StringBuilder argBuilder = new StringBuilder();
1525             final int endIndex = numAttrs * 2;
1526             for (int i = startIndex; i < endIndex; i++) {
1527                 argBuilder.setLength(0);
1528                 if (!mSupplied[i % numAttrs]) {
1529                     final String paramType = mKey.parameterTypes[i % numAttrs];
1530                     final String defaultValue = modelAnalyzer.getDefaultValue(paramType);
1531                     argBuilder.append('(')
1532                             .append(paramType)
1533                             .append(')')
1534                             .append(defaultValue);
1535                 } else {
1536                     if (mConverters[i % numAttrs] != null) {
1537                         final MethodDescription converter = mConverters[i % numAttrs];
1538                         argBuilder.append(converter.type)
1539                                 .append('.')
1540                                 .append(converter.method)
1541                                 .append('(')
1542                                 .append(valueExpressions[attrIndex])
1543                                 .append(')');
1544                     } else {
1545                         if (mCasts[i % numAttrs] != null) {
1546                             argBuilder.append('(')
1547                                     .append(mCasts[i % numAttrs])
1548                                     .append(')');
1549                         }
1550                         argBuilder.append(valueExpressions[attrIndex]);
1551                     }
1552                     attrIndex++;
1553                 }
1554                 args[i - startIndex] = argBuilder.toString();
1555             }
1556             return createAdapterCall(mAdapter, componentExpression, viewExpression, args);
1557         }
1558 
1559         @Override
getMinApi()1560         public int getMinApi() {
1561             return 1;
1562         }
1563 
1564         @Override
requiresOldValue()1565         public boolean requiresOldValue() {
1566             return mAdapter.requiresOldValue;
1567         }
1568 
1569         @Override
getParameterTypes()1570         public ModelClass[] getParameterTypes() {
1571             ModelClass[] parameters = new ModelClass[attributes.length];
1572             String[] paramTypeStrings = mKey.parameterTypes;
1573             ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
1574             int attrIndex = 0;
1575             for (int i = 0; i < mKey.attributes.length; i++) {
1576                 if (mSupplied[i]) {
1577                     parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null);
1578                 }
1579             }
1580             return parameters;
1581         }
1582 
1583         @Override
getBindingAdapterInstanceClass()1584         public String getBindingAdapterInstanceClass() {
1585             return mAdapter.isStatic ? null : mAdapter.type;
1586         }
1587 
1588         @Override
toString()1589         public String toString() {
1590             return "MultiAttributeSetter{" +
1591                     "attributes=" + Arrays.toString(attributes) +
1592                     ", mAdapter=" + mAdapter +
1593                     ", mConverters=" + Arrays.toString(mConverters) +
1594                     ", mCasts=" + Arrays.toString(mCasts) +
1595                     ", mKey=" + mKey +
1596                     '}';
1597         }
1598     }
1599 
1600     public static class ViewDataBindingEventSetter implements BindingSetterCall {
1601 
ViewDataBindingEventSetter()1602         public ViewDataBindingEventSetter() {
1603         }
1604 
1605         @Override
toJava(String componentExpression, String viewExpression, String... valueExpressions)1606         public String toJava(String componentExpression, String viewExpression,
1607                 String... valueExpressions) {
1608             return "setBindingInverseListener(" + viewExpression + ", " +
1609                     valueExpressions[0] + ", " + valueExpressions[1] + ")";
1610         }
1611 
1612         @Override
getMinApi()1613         public int getMinApi() {
1614             return 0;
1615         }
1616 
1617         @Override
requiresOldValue()1618         public boolean requiresOldValue() {
1619             return true;
1620         }
1621 
1622         @Override
getParameterTypes()1623         public ModelClass[] getParameterTypes() {
1624             ModelClass[] parameterTypes = new ModelClass[1];
1625             parameterTypes[0] = ModelAnalyzer.getInstance().findClass(
1626                     "android.databinding.ViewDataBinder.PropertyChangedInverseListener", null);
1627             return parameterTypes;
1628         }
1629 
1630         @Override
getBindingAdapterInstanceClass()1631         public String getBindingAdapterInstanceClass() {
1632             return null;
1633         }
1634     }
1635 
1636     public interface BindingGetterCall {
toJava(String componentExpression, String viewExpression)1637         String toJava(String componentExpression, String viewExpression);
1638 
getGetterType()1639         String getGetterType();
1640 
getMinApi()1641         int getMinApi();
1642 
getBindingAdapterInstanceClass()1643         String getBindingAdapterInstanceClass();
1644 
setBindingAdapterCall(String method)1645         void setBindingAdapterCall(String method);
1646 
getEvent()1647         BindingSetterCall getEvent();
1648 
getEventAttribute()1649         String getEventAttribute();
1650     }
1651 
1652     public static class ViewDataBindingGetterCall implements BindingGetterCall {
1653         private final String mGetter;
1654         private final BindingSetterCall mEventSetter;
1655         private final String mAttribute;
1656         private final ModelClass mBindingClass;
1657 
ViewDataBindingGetterCall(ModelClass bindingClass, String attribute)1658         public ViewDataBindingGetterCall(ModelClass bindingClass, String attribute) {
1659             final int colonIndex = attribute.indexOf(':');
1660             mAttribute = attribute.substring(colonIndex + 1);
1661             mGetter = "get" + StringUtils.capitalize(mAttribute);
1662             mEventSetter = new ViewDataBindingEventSetter();
1663             mBindingClass = bindingClass;
1664         }
1665 
1666         @Override
toJava(String componentExpression, String viewExpression)1667         public String toJava(String componentExpression, String viewExpression) {
1668             return viewExpression + "." + mGetter + "()";
1669         }
1670 
1671         @Override
getGetterType()1672         public String getGetterType() {
1673             return mBindingClass.findInstanceGetter(mGetter).getReturnType().toJavaCode();
1674         }
1675 
1676         @Override
getMinApi()1677         public int getMinApi() {
1678             return 0;
1679         }
1680 
1681         @Override
getBindingAdapterInstanceClass()1682         public String getBindingAdapterInstanceClass() {
1683             return null;
1684         }
1685 
1686         @Override
setBindingAdapterCall(String method)1687         public void setBindingAdapterCall(String method) {
1688         }
1689 
1690         @Override
getEvent()1691         public BindingSetterCall getEvent() {
1692             return mEventSetter;
1693         }
1694 
1695         @Override
getEventAttribute()1696         public String getEventAttribute() {
1697             return mAttribute;
1698         }
1699     }
1700 
1701     public static class ViewGetterCall implements BindingGetterCall {
1702         private final InverseDescription mInverseDescription;
1703         private final BindingSetterCall mEventCall;
1704         private final ModelMethod mMethod;
1705 
ViewGetterCall(InverseDescription inverseDescription, ModelMethod method, BindingSetterCall eventCall)1706         public ViewGetterCall(InverseDescription inverseDescription, ModelMethod method,
1707                 BindingSetterCall eventCall) {
1708             mInverseDescription = inverseDescription;
1709             mEventCall = eventCall;
1710             mMethod = method;
1711         }
1712 
1713         @Override
getEvent()1714         public BindingSetterCall getEvent() {
1715             return mEventCall;
1716         }
1717 
1718         @Override
getEventAttribute()1719         public String getEventAttribute() {
1720             return mInverseDescription.event;
1721         }
1722 
1723         @Override
toJava(String componentExpression, String viewExpression)1724         public String toJava(String componentExpression, String viewExpression) {
1725             return viewExpression + "." + mMethod.getName() + "()";
1726         }
1727 
1728         @Override
getGetterType()1729         public String getGetterType() {
1730             return mMethod.getReturnType().toJavaCode();
1731         }
1732 
1733         @Override
getMinApi()1734         public int getMinApi() {
1735             return mMethod.getMinApi();
1736         }
1737 
1738         @Override
getBindingAdapterInstanceClass()1739         public String getBindingAdapterInstanceClass() {
1740             return null;
1741         }
1742 
1743         @Override
setBindingAdapterCall(String method)1744         public void setBindingAdapterCall(String method) {
1745         }
1746     }
1747 
1748     public static class AdapterGetter implements BindingGetterCall {
1749         private final InverseDescription mInverseDescription;
1750         private String mBindingAdapterCall;
1751         private final BindingSetterCall mEventCall;
1752         private final String mGetterType;
1753 
AdapterGetter(InverseDescription description, BindingSetterCall eventCall, String getterType)1754         public AdapterGetter(InverseDescription description, BindingSetterCall eventCall,
1755                 String getterType) {
1756             mInverseDescription = description;
1757             mEventCall = eventCall;
1758             mGetterType = getterType;
1759         }
1760 
1761         @Override
getGetterType()1762         public String getGetterType() {
1763             return mGetterType;
1764         }
1765 
1766         @Override
toJava(String componentExpression, String viewExpression)1767         public String toJava(String componentExpression, String viewExpression) {
1768             StringBuilder sb = new StringBuilder();
1769 
1770             if (mInverseDescription.isStatic) {
1771                 sb.append(mInverseDescription.type);
1772             } else {
1773                 sb.append(componentExpression).append('.').append(mBindingAdapterCall);
1774             }
1775             sb.append('.').append(mInverseDescription.method).append('(');
1776             if (mInverseDescription.componentClass != null) {
1777                 if (!"DataBindingComponent".equals(mInverseDescription.componentClass)) {
1778                     sb.append('(').append(mInverseDescription.componentClass).append(") ");
1779                 }
1780                 sb.append(componentExpression).append(", ");
1781             }
1782             sb.append(viewExpression).append(')');
1783             return sb.toString();
1784         }
1785 
1786         @Override
getMinApi()1787         public int getMinApi() {
1788             return 1;
1789         }
1790 
1791         @Override
getBindingAdapterInstanceClass()1792         public String getBindingAdapterInstanceClass() {
1793             return mInverseDescription.isStatic ? null : mInverseDescription.type;
1794         }
1795 
1796         @Override
setBindingAdapterCall(String method)1797         public void setBindingAdapterCall(String method) {
1798             mBindingAdapterCall = method;
1799         }
1800 
1801         @Override
getEvent()1802         public BindingSetterCall getEvent() {
1803             return mEventCall;
1804         }
1805 
1806         @Override
getEventAttribute()1807         public String getEventAttribute() {
1808             return mInverseDescription.event;
1809         }
1810     }
1811 
1812     private static class InverseMethod {
1813         public BindingGetterCall call;
1814         public ModelClass returnType;
1815         public ModelClass viewType;
1816 
InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType)1817         public InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType) {
1818             this.call = call;
1819             this.returnType = returnType;
1820             this.viewType = viewType;
1821         }
1822     }
1823 }
1824