13561e3e665698843b1c664385a842e779198960bGeorge Mount/*
23561e3e665698843b1c664385a842e779198960bGeorge Mount * Copyright (C) 2015 The Android Open Source Project
33561e3e665698843b1c664385a842e779198960bGeorge Mount *
43561e3e665698843b1c664385a842e779198960bGeorge Mount * Licensed under the Apache License, Version 2.0 (the "License");
53561e3e665698843b1c664385a842e779198960bGeorge Mount * you may not use this file except in compliance with the License.
63561e3e665698843b1c664385a842e779198960bGeorge Mount * You may obtain a copy of the License at
73561e3e665698843b1c664385a842e779198960bGeorge Mount *
83561e3e665698843b1c664385a842e779198960bGeorge Mount *      http://www.apache.org/licenses/LICENSE-2.0
93561e3e665698843b1c664385a842e779198960bGeorge Mount *
103561e3e665698843b1c664385a842e779198960bGeorge Mount * Unless required by applicable law or agreed to in writing, software
113561e3e665698843b1c664385a842e779198960bGeorge Mount * distributed under the License is distributed on an "AS IS" BASIS,
123561e3e665698843b1c664385a842e779198960bGeorge Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133561e3e665698843b1c664385a842e779198960bGeorge Mount * See the License for the specific language governing permissions and
143561e3e665698843b1c664385a842e779198960bGeorge Mount * limitations under the License.
153561e3e665698843b1c664385a842e779198960bGeorge Mount */
16fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.store;
1797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
18d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.InverseBindingListener;
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelMethod;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.GenerationalClassUtil;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
242611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyarimport android.databinding.tool.util.Preconditions;
254ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport android.databinding.tool.util.StringUtils;
26fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount
273561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.io.IOException;
283561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.io.Serializable;
293561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.util.ArrayList;
30a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.Arrays;
31cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mountimport java.util.Collections;
32cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mountimport java.util.Comparator;
333561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.util.HashMap;
34a128d1c99ea98bb48c45d648906652e3d618d513George Mountimport java.util.HashSet;
35cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mountimport java.util.Iterator;
363561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.util.List;
37a7e767e576adebcddf043ad34ccb8dd167f777b3George Mountimport java.util.Map;
383561e3e665698843b1c664385a842e779198960bGeorge Mountimport java.util.Set;
39cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mountimport java.util.TreeMap;
403561e3e665698843b1c664385a842e779198960bGeorge Mount
413561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.annotation.processing.ProcessingEnvironment;
423561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.element.ExecutableElement;
43e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mountimport javax.lang.model.element.Modifier;
443561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.element.TypeElement;
453561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.element.VariableElement;
463561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.type.ArrayType;
473561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.type.DeclaredType;
48b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mountimport javax.lang.model.type.TypeKind;
493561e3e665698843b1c664385a842e779198960bGeorge Mountimport javax.lang.model.type.TypeMirror;
503561e3e665698843b1c664385a842e779198960bGeorge Mount
513561e3e665698843b1c664385a842e779198960bGeorge Mountpublic class SetterStore {
523561e3e665698843b1c664385a842e779198960bGeorge Mount    private static SetterStore sStore;
533561e3e665698843b1c664385a842e779198960bGeorge Mount
54d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private final IntermediateV2 mStore;
5579fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    private final ModelAnalyzer mClassAnalyzer;
56a128d1c99ea98bb48c45d648906652e3d618d513George Mount    private HashMap<String, List<String>> mInstanceAdapters;
57d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private final HashSet<String> mInverseEventAttributes = new HashSet<String>();
583561e3e665698843b1c664385a842e779198960bGeorge Mount
59cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS =
60cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            new Comparator<MultiAttributeSetter>() {
61cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                @Override
62cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                public int compare(MultiAttributeSetter o1, MultiAttributeSetter o2) {
63cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    if (o1.attributes.length != o2.attributes.length) {
64cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        return o2.attributes.length - o1.attributes.length;
65cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
66716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    ModelClass view1 = mClassAnalyzer.findClass(o1.mKey.viewType, null).erasure();
67716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    ModelClass view2 = mClassAnalyzer.findClass(o2.mKey.viewType, null).erasure();
68cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    if (!view1.equals(view2)) {
69cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (view1.isAssignableFrom(view2)) {
70cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return 1;
71cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else {
72cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return -1;
73cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
74cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
75cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    if (!o1.mKey.attributeIndices.keySet()
76cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            .equals(o2.mKey.attributeIndices.keySet())) {
77cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        // order by attribute name
78cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        Iterator<String> o1Keys = o1.mKey.attributeIndices.keySet().iterator();
79cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        Iterator<String> o2Keys = o2.mKey.attributeIndices.keySet().iterator();
80cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        while (o1Keys.hasNext()) {
81cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            String key1 = o1Keys.next();
82cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            String key2 = o2Keys.next();
83cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            int compare = key1.compareTo(key2);
84cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            if (compare != 0) {
85cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                return compare;
86cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            }
87cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
882611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar                        Preconditions.check(false,
89cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                "The sets don't match! That means the keys shouldn't match also");
90cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
91cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    // Same view type. Same attributes
92cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    for (String attribute : o1.mKey.attributeIndices.keySet()) {
93cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        final int index1 = o1.mKey.attributeIndices.get(attribute);
94cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        final int index2 = o2.mKey.attributeIndices.get(attribute);
95cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        ModelClass type1 = mClassAnalyzer
96cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                .findClass(o1.mKey.parameterTypes[index1], null);
97cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        ModelClass type2 = mClassAnalyzer
98cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                .findClass(o2.mKey.parameterTypes[index2], null);
99cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (type1.equals(type2)) {
100cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            continue;
101cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
102cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (o1.mCasts[index1] != null) {
103cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            if (o2.mCasts[index2] == null) {
104cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                return 1; // o2 is better
105cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            } else {
106cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                continue; // both are casts
107cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            }
108cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else if (o2.mCasts[index2] != null) {
109cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return -1; // o1 is better
110cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
111cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (o1.mConverters[index1] != null) {
112cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            if (o2.mConverters[index2] == null) {
113cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                return 1; // o2 is better
114cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            } else {
115cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                continue; // both are conversions
116cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            }
117cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else if (o2.mConverters[index2] != null) {
118cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return -1; // o1 is better
119cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
120cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
121cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (type1.isPrimitive()) {
122cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            if (type2.isPrimitive()) {
123cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                int type1ConversionLevel = ModelMethod
124cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                        .getImplicitConversionLevel(type1);
125cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                int type2ConversionLevel = ModelMethod
126cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                        .getImplicitConversionLevel(type2);
127cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                return type2ConversionLevel - type1ConversionLevel;
128cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            } else {
129cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                // type1 is primitive and has higher priority
130cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                return -1;
131cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            }
132cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else if (type2.isPrimitive()) {
133cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return 1;
134cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
135cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (type1.isAssignableFrom(type2)) {
136cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return 1;
137cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else if (type2.isAssignableFrom(type1)) {
138cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return -1;
139cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
140cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
141cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    // hmmm... same view type, same attributes, same parameter types... ?
142cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    return 0;
143cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                }
144cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            };
145cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
146d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store) {
14779fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount        mClassAnalyzer = modelAnalyzer;
1484711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        mStore = store;
149d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (HashMap<AccessorKey, InverseDescription> adapter : mStore.inverseAdapters.values()) {
150d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (InverseDescription inverseDescription : adapter.values()) {
151d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                mInverseEventAttributes.add(inverseDescription.event);
152d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
153d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
154d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (HashMap<String, InverseDescription> method : mStore.inverseMethods.values()) {
155d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (InverseDescription inverseDescription : method.values()) {
156d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                mInverseEventAttributes.add(inverseDescription.event);
157d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
158d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1593561e3e665698843b1c664385a842e779198960bGeorge Mount    }
1603561e3e665698843b1c664385a842e779198960bGeorge Mount
16179fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    public static SetterStore get(ModelAnalyzer modelAnalyzer) {
1623561e3e665698843b1c664385a842e779198960bGeorge Mount        if (sStore == null) {
163e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            sStore = load(modelAnalyzer);
1643561e3e665698843b1c664385a842e779198960bGeorge Mount        }
1653561e3e665698843b1c664385a842e779198960bGeorge Mount        return sStore;
1663561e3e665698843b1c664385a842e779198960bGeorge Mount    }
1673561e3e665698843b1c664385a842e779198960bGeorge Mount
168e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static SetterStore load(ModelAnalyzer modelAnalyzer) {
169d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        IntermediateV2 store = new IntermediateV2();
170a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        List<Intermediate> previousStores = GenerationalClassUtil
171e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                .loadObjects(GenerationalClassUtil.ExtensionFilter.SETTER_STORE);
172a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (Intermediate intermediate : previousStores) {
173a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            merge(store, intermediate);
1743561e3e665698843b1c664385a842e779198960bGeorge Mount        }
17579fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount        return new SetterStore(modelAnalyzer, store);
1763561e3e665698843b1c664385a842e779198960bGeorge Mount    }
1773561e3e665698843b1c664385a842e779198960bGeorge Mount
1783561e3e665698843b1c664385a842e779198960bGeorge Mount    public void addRenamedMethod(String attribute, String declaringClass, String method,
1793561e3e665698843b1c664385a842e779198960bGeorge Mount            TypeElement declaredOn) {
1801331801c598a377a2c16e1aed8f975b728adc06eGeorge Mount        attribute = stripNamespace(attribute);
1814711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
1823561e3e665698843b1c664385a842e779198960bGeorge Mount        if (renamed == null) {
183895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar            renamed = new HashMap<String, MethodDescription>();
1844711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount            mStore.renamedMethods.put(attribute, renamed);
1853561e3e665698843b1c664385a842e779198960bGeorge Mount        }
186e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        MethodDescription methodDescription = new MethodDescription(
187e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                declaredOn.getQualifiedName().toString(), method);
188a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        L.d("STORE addmethod desc %s", methodDescription);
1893561e3e665698843b1c664385a842e779198960bGeorge Mount        renamed.put(declaringClass, methodDescription);
1903561e3e665698843b1c664385a842e779198960bGeorge Mount    }
1913561e3e665698843b1c664385a842e779198960bGeorge Mount
192d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public void addInverseMethod(String attribute, String event, String declaringClass,
193d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            String method, TypeElement declaredOn) {
194d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        attribute = stripNamespace(attribute);
195d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        event = stripNamespace(event);
196d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
197d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (inverseMethods == null) {
198d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            inverseMethods = new HashMap<String, InverseDescription>();
199d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mStore.inverseMethods.put(attribute, inverseMethods);
200d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
201d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        InverseDescription methodDescription = new InverseDescription(
202d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                declaredOn.getQualifiedName().toString(), method, event);
203d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        L.d("STORE addInverseMethod desc %s", methodDescription);
204d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        inverseMethods.put(declaringClass, methodDescription);
205d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
206d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
207b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute,
208e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            ExecutableElement bindingMethod, boolean takesComponent) {
209cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        attribute = stripNamespace(attribute);
210a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod);
2114711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
2123561e3e665698843b1c664385a842e779198960bGeorge Mount
2133561e3e665698843b1c664385a842e779198960bGeorge Mount        if (adapters == null) {
214895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar            adapters = new HashMap<AccessorKey, MethodDescription>();
2154711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount            mStore.adapterMethods.put(attribute, adapters);
2163561e3e665698843b1c664385a842e779198960bGeorge Mount        }
2173561e3e665698843b1c664385a842e779198960bGeorge Mount        List<? extends VariableElement> parameters = bindingMethod.getParameters();
218e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        final int viewIndex = takesComponent ? 1 : 0;
219e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
220b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        String view = getQualifiedName(viewType);
221e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        TypeMirror parameterType = eraseType(processingEnv, parameters.get(viewIndex + 1).asType());
222b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        String value = getQualifiedName(parameterType);
2233561e3e665698843b1c664385a842e779198960bGeorge Mount
2243561e3e665698843b1c664385a842e779198960bGeorge Mount        AccessorKey key = new AccessorKey(view, value);
2253561e3e665698843b1c664385a842e779198960bGeorge Mount        if (adapters.containsKey(key)) {
2263561e3e665698843b1c664385a842e779198960bGeorge Mount            throw new IllegalArgumentException("Already exists!");
2273561e3e665698843b1c664385a842e779198960bGeorge Mount        }
2283561e3e665698843b1c664385a842e779198960bGeorge Mount
229e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent));
2303561e3e665698843b1c664385a842e779198960bGeorge Mount    }
2313561e3e665698843b1c664385a842e779198960bGeorge Mount
232d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public void addInverseAdapter(ProcessingEnvironment processingEnv, String attribute,
233d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            String event, ExecutableElement bindingMethod, boolean takesComponent) {
234d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        attribute = stripNamespace(attribute);
235d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        event = stripNamespace(event);
236d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        L.d("STORE addInverseAdapter %s %s", attribute, bindingMethod);
237d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
238d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
239d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (adapters == null) {
240d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            adapters = new HashMap<AccessorKey, InverseDescription>();
241d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mStore.inverseAdapters.put(attribute, adapters);
242d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
243d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        List<? extends VariableElement> parameters = bindingMethod.getParameters();
244d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final int viewIndex = takesComponent ? 1 : 0;
245d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
246d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String view = getQualifiedName(viewType);
247d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        TypeMirror returnType = eraseType(processingEnv, bindingMethod.getReturnType());
248d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String value = getQualifiedName(returnType);
249d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
250d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        AccessorKey key = new AccessorKey(view, value);
251d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (adapters.containsKey(key)) {
252d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            throw new IllegalArgumentException("Already exists!");
253d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
254d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
255d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        adapters.put(key, new InverseDescription(bindingMethod, event, takesComponent));
256d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
257d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
258b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    private static TypeMirror eraseType(ProcessingEnvironment processingEnv,
259b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            TypeMirror typeMirror) {
260b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (hasTypeVar(typeMirror)) {
261b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return processingEnv.getTypeUtils().erasure(typeMirror);
262b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else {
263b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return typeMirror;
264b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
265b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    }
266b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
267b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    private static ModelClass eraseType(ModelClass modelClass) {
268b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (hasTypeVar(modelClass)) {
269b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return modelClass.erasure();
270b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else {
271b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return modelClass;
272b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
273b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    }
274b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
275b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    private static boolean hasTypeVar(TypeMirror typeMirror) {
276b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        TypeKind kind = typeMirror.getKind();
277b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (kind == TypeKind.TYPEVAR) {
278b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return true;
279b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else if (kind == TypeKind.ARRAY) {
280b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return hasTypeVar(((ArrayType) typeMirror).getComponentType());
281b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else if (kind == TypeKind.DECLARED) {
282b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            DeclaredType declaredType = (DeclaredType) typeMirror;
283b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
284b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            if (typeArguments == null || typeArguments.isEmpty()) {
285b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                return false;
286b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
287b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            for (TypeMirror arg : typeArguments) {
288b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                if (hasTypeVar(arg)) {
289b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                    return true;
290b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                }
291b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
292b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return false;
293b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else {
294b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return false;
295b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
296b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    }
297b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
298b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    private static boolean hasTypeVar(ModelClass type) {
299b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (type.isTypeVar()) {
300b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return true;
301b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else if (type.isArray()) {
302b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return hasTypeVar(type.getComponentType());
303b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        } else {
304b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            List<ModelClass> typeArguments = type.getTypeArguments();
305b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            if (typeArguments == null) {
306b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                return false;
307b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
308b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            for (ModelClass arg : typeArguments) {
309b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                if (hasTypeVar(arg)) {
310b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                    return true;
311b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                }
312b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
313b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return false;
314b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
315b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    }
316b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
317b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes,
31896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll) {
319cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod);
320b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod,
32196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                attributes, takesComponent, requireAll);
32220c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        MethodDescription methodDescription = new MethodDescription(bindingMethod,
323e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                attributes.length, takesComponent);
324cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        mStore.multiValueAdapters.put(key, methodDescription);
325cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
326cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
327cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private static String[] stripAttributes(String[] attributes) {
328cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        String[] strippedAttributes = new String[attributes.length];
329cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        for (int i = 0; i < attributes.length; i++) {
330d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (attributes[i] != null) {
331d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                strippedAttributes[i] = stripNamespace(attributes[i]);
332d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
333cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
334cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        return strippedAttributes;
335cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
336cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
33700da715547ee7d5d38a3b8576090ca427a94daa5George Mount    public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) {
338a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn);
33900da715547ee7d5d38a3b8576090ca427a94daa5George Mount        String declaredType = declaredOn.getQualifiedName().toString();
34000da715547ee7d5d38a3b8576090ca427a94daa5George Mount        for (String type : typeNames) {
34100da715547ee7d5d38a3b8576090ca427a94daa5George Mount            mStore.untaggableTypes.put(type, declaredType);
34200da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
34300da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
34400da715547ee7d5d38a3b8576090ca427a94daa5George Mount
3453561e3e665698843b1c664385a842e779198960bGeorge Mount    private static String getQualifiedName(TypeMirror type) {
346bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        final TypeKind kind = type.getKind();
347bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        if (kind == TypeKind.ARRAY) {
348e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return getQualifiedName(((ArrayType) type).getComponentType()) + "[]";
349bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        } else if (kind == TypeKind.DECLARED && isIncompleteType(type)) {
350bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            DeclaredType declaredType = (DeclaredType) type;
351bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            return declaredType.asElement().toString();
352e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        } else {
353e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return type.toString();
3543561e3e665698843b1c664385a842e779198960bGeorge Mount        }
3553561e3e665698843b1c664385a842e779198960bGeorge Mount    }
3563561e3e665698843b1c664385a842e779198960bGeorge Mount
357bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    private static boolean isIncompleteType(TypeMirror type) {
358bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        final TypeKind kind = type.getKind();
359bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        if (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD) {
360bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            return true;
361bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        } else if (kind == TypeKind.DECLARED) {
362bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            DeclaredType declaredType = (DeclaredType) type;
363bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments();
364bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            if (typeArgs == null) {
365bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                return false;
366bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            }
367bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            for (TypeMirror arg : typeArgs) {
368bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                if (isIncompleteType(arg)) {
369bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                    return true;
370bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                }
371bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            }
372bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        }
373bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        return false;
374bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    }
375bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount
3763561e3e665698843b1c664385a842e779198960bGeorge Mount    public void addConversionMethod(ExecutableElement conversionMethod) {
377a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        L.d("STORE addConversionMethod %s", conversionMethod);
3783561e3e665698843b1c664385a842e779198960bGeorge Mount        List<? extends VariableElement> parameters = conversionMethod.getParameters();
3793561e3e665698843b1c664385a842e779198960bGeorge Mount        String fromType = getQualifiedName(parameters.get(0).asType());
3803561e3e665698843b1c664385a842e779198960bGeorge Mount        String toType = getQualifiedName(conversionMethod.getReturnType());
381e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        MethodDescription methodDescription = new MethodDescription(conversionMethod, 1, false);
3824711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType);
3833561e3e665698843b1c664385a842e779198960bGeorge Mount        if (convertTo == null) {
384895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar            convertTo = new HashMap<String, MethodDescription>();
3854711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount            mStore.conversionMethods.put(fromType, convertTo);
3863561e3e665698843b1c664385a842e779198960bGeorge Mount        }
3873561e3e665698843b1c664385a842e779198960bGeorge Mount        convertTo.put(toType, methodDescription);
3883561e3e665698843b1c664385a842e779198960bGeorge Mount    }
3893561e3e665698843b1c664385a842e779198960bGeorge Mount
3903561e3e665698843b1c664385a842e779198960bGeorge Mount    public void clear(Set<String> classes) {
391895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>();
3924711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) {
3933561e3e665698843b1c664385a842e779198960bGeorge Mount            for (AccessorKey key : adapters.keySet()) {
3943561e3e665698843b1c664385a842e779198960bGeorge Mount                MethodDescription description = adapters.get(key);
3953561e3e665698843b1c664385a842e779198960bGeorge Mount                if (classes.contains(description.type)) {
3963561e3e665698843b1c664385a842e779198960bGeorge Mount                    removedAccessorKeys.add(key);
3973561e3e665698843b1c664385a842e779198960bGeorge Mount                }
3983561e3e665698843b1c664385a842e779198960bGeorge Mount            }
39900da715547ee7d5d38a3b8576090ca427a94daa5George Mount            removeFromMap(adapters, removedAccessorKeys);
4003561e3e665698843b1c664385a842e779198960bGeorge Mount        }
4013561e3e665698843b1c664385a842e779198960bGeorge Mount
402895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        ArrayList<String> removedRenamed = new ArrayList<String>();
4034711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) {
4043561e3e665698843b1c664385a842e779198960bGeorge Mount            for (String key : renamed.keySet()) {
4053561e3e665698843b1c664385a842e779198960bGeorge Mount                if (classes.contains(renamed.get(key).type)) {
4063561e3e665698843b1c664385a842e779198960bGeorge Mount                    removedRenamed.add(key);
4073561e3e665698843b1c664385a842e779198960bGeorge Mount                }
4083561e3e665698843b1c664385a842e779198960bGeorge Mount            }
40900da715547ee7d5d38a3b8576090ca427a94daa5George Mount            removeFromMap(renamed, removedRenamed);
4103561e3e665698843b1c664385a842e779198960bGeorge Mount        }
4113561e3e665698843b1c664385a842e779198960bGeorge Mount
412895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        ArrayList<String> removedConversions = new ArrayList<String>();
4134711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) {
4143561e3e665698843b1c664385a842e779198960bGeorge Mount            for (String toType : convertTos.keySet()) {
4153561e3e665698843b1c664385a842e779198960bGeorge Mount                MethodDescription methodDescription = convertTos.get(toType);
4163561e3e665698843b1c664385a842e779198960bGeorge Mount                if (classes.contains(methodDescription.type)) {
4173561e3e665698843b1c664385a842e779198960bGeorge Mount                    removedConversions.add(toType);
4183561e3e665698843b1c664385a842e779198960bGeorge Mount                }
4193561e3e665698843b1c664385a842e779198960bGeorge Mount            }
42000da715547ee7d5d38a3b8576090ca427a94daa5George Mount            removeFromMap(convertTos, removedConversions);
42100da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
42200da715547ee7d5d38a3b8576090ca427a94daa5George Mount
423895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        ArrayList<String> removedUntaggable = new ArrayList<String>();
42400da715547ee7d5d38a3b8576090ca427a94daa5George Mount        for (String typeName : mStore.untaggableTypes.keySet()) {
42500da715547ee7d5d38a3b8576090ca427a94daa5George Mount            if (classes.contains(mStore.untaggableTypes.get(typeName))) {
42600da715547ee7d5d38a3b8576090ca427a94daa5George Mount                removedUntaggable.add(typeName);
4273561e3e665698843b1c664385a842e779198960bGeorge Mount            }
4283561e3e665698843b1c664385a842e779198960bGeorge Mount        }
42900da715547ee7d5d38a3b8576090ca427a94daa5George Mount        removeFromMap(mStore.untaggableTypes, removedUntaggable);
43000da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
43100da715547ee7d5d38a3b8576090ca427a94daa5George Mount
43200da715547ee7d5d38a3b8576090ca427a94daa5George Mount    private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) {
43300da715547ee7d5d38a3b8576090ca427a94daa5George Mount        for (K key : keys) {
43400da715547ee7d5d38a3b8576090ca427a94daa5George Mount            map.remove(key);
43500da715547ee7d5d38a3b8576090ca427a94daa5George Mount        }
43600da715547ee7d5d38a3b8576090ca427a94daa5George Mount        keys.clear();
4373561e3e665698843b1c664385a842e779198960bGeorge Mount    }
4383561e3e665698843b1c664385a842e779198960bGeorge Mount
439a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public void write(String projectPackage, ProcessingEnvironment processingEnvironment)
440a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            throws IOException {
441a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
442e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                projectPackage, projectPackage +
443e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        GenerationalClassUtil.ExtensionFilter.SETTER_STORE.getExtension(), mStore);
4443561e3e665698843b1c664385a842e779198960bGeorge Mount    }
4453561e3e665698843b1c664385a842e779198960bGeorge Mount
446cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private static String stripNamespace(String attribute) {
4477a25150b56cb9256f9f4786f47694b03ba312d72George Mount        if (!attribute.startsWith("android:")) {
4487a25150b56cb9256f9f4786f47694b03ba312d72George Mount            int colon = attribute.indexOf(':');
4497a25150b56cb9256f9f4786f47694b03ba312d72George Mount            if (colon >= 0) {
4507a25150b56cb9256f9f4786f47694b03ba312d72George Mount                attribute = attribute.substring(colon + 1);
4517a25150b56cb9256f9f4786f47694b03ba312d72George Mount            }
4527a25150b56cb9256f9f4786f47694b03ba312d72George Mount        }
453cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        return attribute;
454cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
455cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
456d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public boolean isTwoWayEventAttribute(String attribute) {
457d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        attribute = stripNamespace(attribute);
458d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return mInverseEventAttributes.contains(attribute);
459d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
460cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes,
461cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            ModelClass viewType, ModelClass[] valueType) {
462cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        attributes = stripAttributes(attributes);
463cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        final ArrayList<MultiAttributeSetter> calls = new ArrayList<MultiAttributeSetter>();
464b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (viewType != null && viewType.isGeneric()) {
465b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            List<ModelClass> viewGenerics = viewType.getTypeArguments();
466b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            for (int i = 0; i < valueType.length; i++) {
467b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                valueType[i] = eraseType(valueType[i], viewGenerics);
468b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
469b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            viewType = viewType.erasure();
470b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
471cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        ArrayList<MultiAttributeSetter> matching = getMatchingMultiAttributeSetters(attributes,
472cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                viewType, valueType);
473cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        Collections.sort(matching, COMPARE_MULTI_ATTRIBUTE_SETTERS);
474cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        while (!matching.isEmpty()) {
475cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            MultiAttributeSetter bestMatch = matching.get(0);
476cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            calls.add(bestMatch);
477cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            removeConsumedAttributes(matching, bestMatch.attributes);
478cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
479cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        return calls;
480cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
481cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
482a128d1c99ea98bb48c45d648906652e3d618d513George Mount    private static String simpleName(String className) {
483a128d1c99ea98bb48c45d648906652e3d618d513George Mount        int dotIndex = className.lastIndexOf('.');
484a128d1c99ea98bb48c45d648906652e3d618d513George Mount        if (dotIndex < 0) {
485a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return className;
486a128d1c99ea98bb48c45d648906652e3d618d513George Mount        } else {
487a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return className.substring(dotIndex + 1);
488a128d1c99ea98bb48c45d648906652e3d618d513George Mount        }
489a128d1c99ea98bb48c45d648906652e3d618d513George Mount    }
490a128d1c99ea98bb48c45d648906652e3d618d513George Mount
491a128d1c99ea98bb48c45d648906652e3d618d513George Mount    public Map<String, List<String>> getComponentBindingAdapters() {
492a128d1c99ea98bb48c45d648906652e3d618d513George Mount        ensureInstanceAdapters();
493a128d1c99ea98bb48c45d648906652e3d618d513George Mount        return mInstanceAdapters;
494a128d1c99ea98bb48c45d648906652e3d618d513George Mount    }
495a128d1c99ea98bb48c45d648906652e3d618d513George Mount
496a128d1c99ea98bb48c45d648906652e3d618d513George Mount    private String getBindingAdapterCall(String className) {
497a128d1c99ea98bb48c45d648906652e3d618d513George Mount        ensureInstanceAdapters();
498a128d1c99ea98bb48c45d648906652e3d618d513George Mount        final String simpleName = simpleName(className);
499a128d1c99ea98bb48c45d648906652e3d618d513George Mount        List<String> adapters = mInstanceAdapters.get(simpleName);
500a128d1c99ea98bb48c45d648906652e3d618d513George Mount        if (adapters.size() == 1) {
501a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return "get" + simpleName + "()";
502a128d1c99ea98bb48c45d648906652e3d618d513George Mount        } else {
503a128d1c99ea98bb48c45d648906652e3d618d513George Mount            int index = adapters.indexOf(className) + 1;
504a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return "get" + simpleName + index + "()";
505a128d1c99ea98bb48c45d648906652e3d618d513George Mount        }
506a128d1c99ea98bb48c45d648906652e3d618d513George Mount    }
507a128d1c99ea98bb48c45d648906652e3d618d513George Mount
508a128d1c99ea98bb48c45d648906652e3d618d513George Mount    private void ensureInstanceAdapters() {
509a128d1c99ea98bb48c45d648906652e3d618d513George Mount        if (mInstanceAdapters == null) {
5109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            HashSet<String> adapters = new HashSet<String>();
511a128d1c99ea98bb48c45d648906652e3d618d513George Mount            for (HashMap<AccessorKey, MethodDescription> methods : mStore.adapterMethods.values()) {
512a128d1c99ea98bb48c45d648906652e3d618d513George Mount                for (MethodDescription method : methods.values()) {
513a128d1c99ea98bb48c45d648906652e3d618d513George Mount                    if (!method.isStatic) {
514a128d1c99ea98bb48c45d648906652e3d618d513George Mount                        adapters.add(method.type);
515a128d1c99ea98bb48c45d648906652e3d618d513George Mount                    }
516a128d1c99ea98bb48c45d648906652e3d618d513George Mount                }
517a128d1c99ea98bb48c45d648906652e3d618d513George Mount            }
518a128d1c99ea98bb48c45d648906652e3d618d513George Mount            for (MethodDescription method : mStore.multiValueAdapters.values()) {
519a128d1c99ea98bb48c45d648906652e3d618d513George Mount                if (!method.isStatic) {
520a128d1c99ea98bb48c45d648906652e3d618d513George Mount                    adapters.add(method.type);
521a128d1c99ea98bb48c45d648906652e3d618d513George Mount                }
522a128d1c99ea98bb48c45d648906652e3d618d513George Mount            }
523d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (Map<AccessorKey, InverseDescription> methods : mStore.inverseAdapters.values()) {
524d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                for (InverseDescription method : methods.values()) {
525d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    if (!method.isStatic) {
526d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        adapters.add(method.type);
527d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    }
528d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
529d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
5309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            mInstanceAdapters = new HashMap<String, List<String>>();
531a128d1c99ea98bb48c45d648906652e3d618d513George Mount            for (String adapter : adapters) {
532a128d1c99ea98bb48c45d648906652e3d618d513George Mount                final String simpleName = simpleName(adapter);
533a128d1c99ea98bb48c45d648906652e3d618d513George Mount                List<String> list = mInstanceAdapters.get(simpleName);
534a128d1c99ea98bb48c45d648906652e3d618d513George Mount                if (list == null) {
5359784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    list = new ArrayList<String>();
536a128d1c99ea98bb48c45d648906652e3d618d513George Mount                    mInstanceAdapters.put(simpleName, list);
537a128d1c99ea98bb48c45d648906652e3d618d513George Mount                }
538a128d1c99ea98bb48c45d648906652e3d618d513George Mount                list.add(adapter);
539a128d1c99ea98bb48c45d648906652e3d618d513George Mount            }
540a128d1c99ea98bb48c45d648906652e3d618d513George Mount            for (List<String> list : mInstanceAdapters.values()) {
541a128d1c99ea98bb48c45d648906652e3d618d513George Mount                if (list.size() > 1) {
542a128d1c99ea98bb48c45d648906652e3d618d513George Mount                    Collections.sort(list);
543a128d1c99ea98bb48c45d648906652e3d618d513George Mount                }
544a128d1c99ea98bb48c45d648906652e3d618d513George Mount            }
545a128d1c99ea98bb48c45d648906652e3d618d513George Mount        }
546a128d1c99ea98bb48c45d648906652e3d618d513George Mount    }
547a128d1c99ea98bb48c45d648906652e3d618d513George Mount
548cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    // Removes all MultiAttributeSetters that require any of the values in attributes
549cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching,
550cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            String[] attributes) {
551cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        for (int i = matching.size() - 1; i >= 0; i--) {
552cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            final MultiAttributeSetter setter = matching.get(i);
553cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            boolean found = false;
554cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            for (String attribute : attributes) {
555cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                if (isInArray(attribute, setter.attributes)) {
556cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    found = true;
557cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    break;
558cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                }
559cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
560cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (found) {
561cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                matching.remove(i);
562cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
563cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
564cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
565cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
566cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    // Linear search through the String array for a specific value.
567cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private static boolean isInArray(String str, String[] array) {
568cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        for (String value : array) {
569cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (value.equals(str)) {
570cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                return true;
571cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
572cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
573cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        return false;
574cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
575cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
576cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private ArrayList<MultiAttributeSetter> getMatchingMultiAttributeSetters(String[] attributes,
577cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            ModelClass viewType, ModelClass[] valueType) {
578cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>();
579cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) {
58096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            if (adapter.requireAll && adapter.attributes.length > attributes.length) {
581cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                continue;
582cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
583b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null);
584b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            if (viewClass.isGeneric()) {
585b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                viewClass = viewClass.erasure();
586b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
587cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (!viewClass.isAssignableFrom(viewType)) {
588cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                continue;
589cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
590cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            final MethodDescription method = mStore.multiValueAdapters.get(adapter);
591cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            final MultiAttributeSetter setter = createMultiAttributeSetter(method, attributes,
592cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    valueType, adapter);
593cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (setter != null) {
594cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                setters.add(setter);
595cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
596cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
597cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        return setters;
598cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
599cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
600cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private MultiAttributeSetter createMultiAttributeSetter(MethodDescription method,
601cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter) {
602cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        int matchingAttributes = 0;
603cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        String[] casts = new String[adapter.attributes.length];
604cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        MethodDescription[] conversions = new MethodDescription[adapter.attributes.length];
60596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount        boolean[] supplied = new boolean[adapter.attributes.length];
606cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
607cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        for (int i = 0; i < allAttributes.length; i++) {
608cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            Integer index = adapter.attributeIndices.get(allAttributes[i]);
609cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (index != null) {
61096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                supplied[index] = true;
611cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                matchingAttributes++;
612cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                final String parameterTypeStr = adapter.parameterTypes[index];
613b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                final ModelClass parameterType = eraseType(
614b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                        mClassAnalyzer.findClass(parameterTypeStr, null));
615cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                final ModelClass attributeType = attributeValues[i];
616cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                if (!parameterType.isAssignableFrom(attributeType)) {
617cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    if (ModelMethod.isBoxingConversion(parameterType, attributeType)) {
618cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        // automatic boxing is ok
619cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        continue;
620cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) {
621cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        // implicit conversion is ok
622cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        continue;
623cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
624cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    // Look for a converter
625cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    conversions[index] = getConversionMethod(attributeType, parameterType, null);
626cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    if (conversions[index] == null) {
627cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        if (attributeType.isObject()) {
628cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            // Cast is allowed also
629cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            casts[index] = parameterTypeStr;
630cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        } else {
631cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            // Parameter type mismatch
632cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                            return null;
633cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                        }
634cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
635cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                }
636cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
637cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
638cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
63996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount        if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) ||
64096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                matchingAttributes == 0) {
641cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            return null;
642cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        } else {
64396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            return new MultiAttributeSetter(adapter, supplied, method, conversions, casts);
644cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
645cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
646cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
647cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    public SetterCall getSetterCall(String attribute, ModelClass viewType,
648cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            ModelClass valueType, Map<String, String> imports) {
649cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        attribute = stripNamespace(attribute);
65097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        SetterCall setterCall = null;
65197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        MethodDescription conversionMethod = null;
6528e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount        if (viewType != null) {
653716ba89e7f459f49ea85070d4710c1d79d715298George Mount            viewType = viewType.erasure();
6548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
6558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports);
6568e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            ModelClass bestViewType = null;
6578e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            ModelClass bestValueType = null;
6588e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            if (bestSetterMethod != null) {
6598e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                bestViewType = bestSetterMethod.getDeclaringClass();
6608e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                bestValueType = bestSetterMethod.getParameterTypes()[0];
66197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                setterCall = new ModelMethodSetter(bestSetterMethod);
6628e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            }
6633561e3e665698843b1c664385a842e779198960bGeorge Mount
6648e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount            if (adapters != null) {
6658e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                for (AccessorKey key : adapters.keySet()) {
6668e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                    try {
6678e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                        ModelClass adapterViewType = mClassAnalyzer
668716ba89e7f459f49ea85070d4710c1d79d715298George Mount                                .findClass(key.viewType, imports).erasure();
669c619d8f69127c1200103d8119101c5f0675661d0George Mount                        if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) {
6708e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                            try {
671b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                L.d("setter parameter type is %s", key.valueType);
672b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                final ModelClass adapterValueType = eraseType(mClassAnalyzer
673b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                        .findClass(key.valueType, imports));
674b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                L.d("setter %s takes type %s, compared to %s",
675b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                        adapters.get(key).method, adapterValueType.toJavaCode(),
676b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                                        valueType.toJavaCode());
6778e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                boolean isBetterView = bestViewType == null ||
678d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        bestViewType.isAssignableFrom(adapterViewType);
6798e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                if (isBetterParameter(valueType, adapterValueType, bestValueType,
6808e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                        isBetterView, imports)) {
6818e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                    bestViewType = adapterViewType;
6828e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                    bestValueType = adapterValueType;
683fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                                    MethodDescription adapter = adapters.get(key);
684716ba89e7f459f49ea85070d4710c1d79d715298George Mount                                    setterCall = new AdapterSetter(adapter, adapterValueType);
6858e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                                }
6861b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
6878e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                            } catch (Exception e) {
68897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                                L.e(e, "Unknown class: %s", key.valueType);
6898e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                            }
690974a07824642fabd896930f20c7b176fd2333e31George Mount                        }
6918e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                    } catch (Exception e) {
69297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        L.e(e, "Unknown class: %s", key.viewType);
6933561e3e665698843b1c664385a842e779198960bGeorge Mount                    }
6943561e3e665698843b1c664385a842e779198960bGeorge Mount                }
6953561e3e665698843b1c664385a842e779198960bGeorge Mount            }
6963561e3e665698843b1c664385a842e779198960bGeorge Mount
69797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            conversionMethod = getConversionMethod(valueType, bestValueType, imports);
698fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) {
699fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                setterCall.setCast(bestValueType);
700fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
7013561e3e665698843b1c664385a842e779198960bGeorge Mount        }
70297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        if (setterCall == null) {
7035f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount            if (viewType != null && !viewType.isViewDataBinding()) {
704716ba89e7f459f49ea85070d4710c1d79d715298George Mount                return null; // no setter found!!
7055f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount            }
70697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            setterCall = new DummySetter(getDefaultSetter(attribute));
7073561e3e665698843b1c664385a842e779198960bGeorge Mount        }
70897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        setterCall.setConverter(conversionMethod);
70997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return setterCall;
7103561e3e665698843b1c664385a842e779198960bGeorge Mount    }
7113561e3e665698843b1c664385a842e779198960bGeorge Mount
712d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public BindingGetterCall getGetterCall(String attribute, ModelClass viewType,
713d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            ModelClass valueType, Map<String, String> imports) {
714d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (viewType == null) {
715d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return null;
716d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        } else if (viewType.isViewDataBinding()) {
717d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return new ViewDataBindingGetterCall(attribute);
718d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
719d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
720d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        attribute = stripNamespace(attribute);
721d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        viewType = viewType.erasure();
722d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
723d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        InverseMethod bestMethod = getBestGetter(viewType, valueType, attribute, imports);
724d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
725d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (adapters != null) {
726d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (AccessorKey key : adapters.keySet()) {
727d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                try {
728d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    ModelClass adapterViewType = mClassAnalyzer
729d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            .findClass(key.viewType, imports).erasure();
730d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) {
731d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        try {
732d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            L.d("getter return type is %s", key.valueType);
733d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            final ModelClass adapterValueType = eraseType(mClassAnalyzer
734d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    .findClass(key.valueType, imports));
735d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            L.d("getter %s returns type %s, compared to %s",
736d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    adapters.get(key).method, adapterValueType.toJavaCode(),
737d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    valueType);
738d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            boolean isBetterView = bestMethod.viewType == null ||
739d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    bestMethod.viewType.isAssignableFrom(adapterViewType);
740d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            if (valueType == null ||
741d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    isBetterParameter(adapterValueType, valueType,
742d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                            bestMethod.returnType, isBetterView, imports)) {
743d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                bestMethod.viewType = adapterViewType;
744d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                bestMethod.returnType = adapterValueType;
745d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                InverseDescription inverseDescription = adapters.get(key);
746d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
747d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        InverseBindingListener.class);
748d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                BindingSetterCall eventCall = getSetterCall(
749d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        inverseDescription.event, viewType, listenerType, imports);
750d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                if (eventCall == null) {
751d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    List<MultiAttributeSetter> setters =
752d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                            getMultiAttributeSetterCalls(
753d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                                    new String[]{inverseDescription.event},
754d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                                    viewType, new ModelClass[] {listenerType});
755d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    if (setters.size() != 1) {
756d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        L.e("Could not find event '%s' on View type '%s'",
757d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                                inverseDescription.event,
758d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                                viewType.getCanonicalName());
759d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    } else {
760d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        bestMethod.call = new AdapterGetter(inverseDescription,
761d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                                setters.get(0));
762d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    }
763d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                } else {
764d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                    bestMethod.call = new AdapterGetter(inverseDescription,
765d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                            eventCall);
766d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                }
767d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            }
768d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
769d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        } catch (Exception e) {
770d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            L.e(e, "Unknown class: %s", key.valueType);
771d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        }
772d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    }
773d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                } catch (Exception e) {
774d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    L.e(e, "Unknown class: %s", key.viewType);
775d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
776d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
777d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
778d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
779d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return bestMethod.call;
780d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
781d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
78200da715547ee7d5d38a3b8576090ca427a94daa5George Mount    public boolean isUntaggable(String viewType) {
78300da715547ee7d5d38a3b8576090ca427a94daa5George Mount        return mStore.untaggableTypes.containsKey(viewType);
78400da715547ee7d5d38a3b8576090ca427a94daa5George Mount    }
78500da715547ee7d5d38a3b8576090ca427a94daa5George Mount
78679fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType,
787a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount            String attribute, Map<String, String> imports) {
78891d538470c011e19fa4375cc3531b5dd9ae01d55George Mount        if (viewType.isGeneric()) {
789b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            argumentType = eraseType(argumentType, viewType.getTypeArguments());
79091d538470c011e19fa4375cc3531b5dd9ae01d55George Mount            viewType = viewType.erasure();
79191d538470c011e19fa4375cc3531b5dd9ae01d55George Mount        }
792895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        List<String> setterCandidates = new ArrayList<String>();
7934711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
7943561e3e665698843b1c664385a842e779198960bGeorge Mount        if (renamed != null) {
7950935fcaf455ce6fc6916194da651b556e1ec0326George Mount            for (String className : renamed.keySet()) {
796974a07824642fabd896930f20c7b176fd2333e31George Mount                try {
797a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                    ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports);
798716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    if (renamedViewType.erasure().isAssignableFrom(viewType)) {
79997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        setterCandidates.add(renamed.get(className).method);
800974a07824642fabd896930f20c7b176fd2333e31George Mount                        break;
801974a07824642fabd896930f20c7b176fd2333e31George Mount                    }
802974a07824642fabd896930f20c7b176fd2333e31George Mount                } catch (Exception e) {
803e4b93061ac703e48fc2c9994c9059ed016f05559George Mount                    //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
8043561e3e665698843b1c664385a842e779198960bGeorge Mount                }
8053561e3e665698843b1c664385a842e779198960bGeorge Mount            }
8063561e3e665698843b1c664385a842e779198960bGeorge Mount        }
80797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        setterCandidates.add(getDefaultSetter(attribute));
80897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        setterCandidates.add(trimAttributeNamespace(attribute));
809e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount
81079fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount        ModelMethod bestMethod = null;
811fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelClass bestParameterType = null;
812fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        List<ModelClass> args = new ArrayList<ModelClass>();
813fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        args.add(argumentType);
81497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        for (String name : setterCandidates) {
81597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            ModelMethod[] methods = viewType.getMethods(name, 1);
81697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
81797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            for (ModelMethod method : methods) {
81897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                ModelClass[] parameterTypes = method.getParameterTypes();
819fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                ModelClass param = parameterTypes[0];
820fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                if (method.isVoid() &&
821fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                        isBetterParameter(argumentType, param, bestParameterType, true, imports)) {
822fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    bestParameterType = param;
823fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    bestMethod = method;
8243561e3e665698843b1c664385a842e779198960bGeorge Mount                }
8253561e3e665698843b1c664385a842e779198960bGeorge Mount            }
8263561e3e665698843b1c664385a842e779198960bGeorge Mount        }
8273561e3e665698843b1c664385a842e779198960bGeorge Mount        return bestMethod;
828b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    }
82997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
830d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private InverseMethod getBestGetter(ModelClass viewType, ModelClass valueType,
831d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            String attribute, Map<String, String> imports) {
832d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (viewType.isGeneric()) {
833d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (valueType != null) {
834d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                valueType = eraseType(valueType, viewType.getTypeArguments());
835d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
836d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            viewType = viewType.erasure();
837d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
838d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        ModelClass bestReturnType = null;
839d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        InverseDescription bestDescription = null;
840d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        ModelClass bestViewType = null;
841d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        ModelMethod bestMethod = null;
842d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
843d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
844d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (inverseMethods != null) {
845d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (String className : inverseMethods.keySet()) {
846d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                try {
847d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    ModelClass methodViewType = mClassAnalyzer.findClass(className, imports);
848d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    if (methodViewType.erasure().isAssignableFrom(viewType)) {
849d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        boolean isBetterViewType = bestViewType == null ||
850d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                bestViewType.isAssignableFrom(methodViewType);
851d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        final InverseDescription inverseDescription = inverseMethods.get(className);
852d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        final String name =  inverseDescription.method.isEmpty() ?
853d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                trimAttributeNamespace(attribute) : inverseDescription.method;
854d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        ModelMethod method = methodViewType.findInstanceGetter(name);
855d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        ModelClass returnType = method.getReturnType(null); // no parameters
856d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        if (valueType == null || bestReturnType == null ||
857d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                isBetterParameter(returnType, valueType, bestReturnType,
858d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                                        isBetterViewType, imports)) {
859d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            bestDescription = inverseDescription;
860d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            bestReturnType = returnType;
861d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            bestViewType = methodViewType;
862d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            bestMethod = method;
863d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        }
864d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    }
865d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                } catch (Exception e) {
866d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
867d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
868d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
869d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
870d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
871d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        BindingGetterCall call = null;
872d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (bestDescription != null) {
873d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            final ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
874d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    InverseBindingListener.class);
875d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            SetterCall eventSetter = getSetterCall(bestDescription.event, viewType,
876d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    listenerType, imports);
877d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (eventSetter == null) {
878d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                List<MultiAttributeSetter> setters = getMultiAttributeSetterCalls(
879d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        new String[] {bestDescription.event}, viewType,
880d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        new ModelClass[] {listenerType});
881d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                if (setters.size() != 1) {
882d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    L.e("Could not find event '%s' on View type '%s'", bestDescription.event,
883d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            viewType.getCanonicalName());
884d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    bestViewType = null;
885d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    bestReturnType = null;
886d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                } else {
887d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    call = new ViewGetterCall(bestDescription, bestMethod, setters.get(0));
888d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
889d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            } else {
890d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                call = new ViewGetterCall(bestDescription, bestMethod, eventSetter);
891d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
892d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
893d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return new InverseMethod(call, bestReturnType, bestViewType);
894d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
895d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
896b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) {
897b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        List<ModelClass> typeArguments = type.getTypeArguments();
898b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        if (typeArguments == null || typeParameters == null) {
899b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            return type;
900b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
901b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        for (ModelClass arg : typeArguments) {
902b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            if (typeParameters.contains(arg)) {
903b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                return type.erasure();
904b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount            }
905b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        }
906b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        return type;
90797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
90897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
90997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private static String trimAttributeNamespace(String attribute) {
91097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        final int colonIndex = attribute.indexOf(':');
91197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1);
9123561e3e665698843b1c664385a842e779198960bGeorge Mount    }
9133561e3e665698843b1c664385a842e779198960bGeorge Mount
9143561e3e665698843b1c664385a842e779198960bGeorge Mount    private static String getDefaultSetter(String attribute) {
91597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute));
9163561e3e665698843b1c664385a842e779198960bGeorge Mount    }
9173561e3e665698843b1c664385a842e779198960bGeorge Mount
91879fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    private boolean isBetterParameter(ModelClass argument, ModelClass parameter,
919a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount            ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) {
9203561e3e665698843b1c664385a842e779198960bGeorge Mount        // Right view type. Check the value
9213561e3e665698843b1c664385a842e779198960bGeorge Mount        if (!isBetterViewTypeMatch && oldParameter.equals(argument)) {
9223561e3e665698843b1c664385a842e779198960bGeorge Mount            return false;
9233561e3e665698843b1c664385a842e779198960bGeorge Mount        } else if (argument.equals(parameter)) {
9243561e3e665698843b1c664385a842e779198960bGeorge Mount            // Exact match
9253561e3e665698843b1c664385a842e779198960bGeorge Mount            return true;
926fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        } else if (!isBetterViewTypeMatch &&
927fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                ModelMethod.isBoxingConversion(oldParameter, argument)) {
9283561e3e665698843b1c664385a842e779198960bGeorge Mount            return false;
929fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        } else if (ModelMethod.isBoxingConversion(parameter, argument)) {
9303561e3e665698843b1c664385a842e779198960bGeorge Mount            // Boxing/unboxing is second best
9313561e3e665698843b1c664385a842e779198960bGeorge Mount            return true;
9323561e3e665698843b1c664385a842e779198960bGeorge Mount        } else {
933fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter);
934fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (ModelMethod.isImplicitConversion(argument, parameter)) {
9353561e3e665698843b1c664385a842e779198960bGeorge Mount                // Better implicit conversion
936fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter);
9373561e3e665698843b1c664385a842e779198960bGeorge Mount                return oldConversionLevel < 0 || conversionLevel < oldConversionLevel;
9383561e3e665698843b1c664385a842e779198960bGeorge Mount            } else if (oldConversionLevel >= 0) {
9393561e3e665698843b1c664385a842e779198960bGeorge Mount                return false;
9403561e3e665698843b1c664385a842e779198960bGeorge Mount            } else if (parameter.isAssignableFrom(argument)) {
9413561e3e665698843b1c664385a842e779198960bGeorge Mount                // Right type, see if it is better than the current best match.
9423561e3e665698843b1c664385a842e779198960bGeorge Mount                if (oldParameter == null) {
9433561e3e665698843b1c664385a842e779198960bGeorge Mount                    return true;
9443561e3e665698843b1c664385a842e779198960bGeorge Mount                } else {
9453561e3e665698843b1c664385a842e779198960bGeorge Mount                    return oldParameter.isAssignableFrom(parameter);
9463561e3e665698843b1c664385a842e779198960bGeorge Mount                }
9473561e3e665698843b1c664385a842e779198960bGeorge Mount            } else {
948a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                MethodDescription conversionMethod = getConversionMethod(argument, parameter,
949a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                        imports);
9505cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                if (conversionMethod != null) {
9515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                    return true;
9525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                }
953a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                if (getConversionMethod(argument, oldParameter, imports) != null) {
9545cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                    return false;
9555cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount                }
956e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount                return argument.isObject() && !parameter.isPrimitive();
9573561e3e665698843b1c664385a842e779198960bGeorge Mount            }
9583561e3e665698843b1c664385a842e779198960bGeorge Mount        }
9593561e3e665698843b1c664385a842e779198960bGeorge Mount    }
9603561e3e665698843b1c664385a842e779198960bGeorge Mount
961a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount    private MethodDescription getConversionMethod(ModelClass from, ModelClass to,
962a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount            Map<String, String> imports) {
9633561e3e665698843b1c664385a842e779198960bGeorge Mount        if (from != null && to != null) {
96497758524d3953793b50e3e0121ef3cbdc047b35bGeorge Mount            if (to.isObject()) {
96597758524d3953793b50e3e0121ef3cbdc047b35bGeorge Mount                return null;
96697758524d3953793b50e3e0121ef3cbdc047b35bGeorge Mount            }
9674711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount            for (String fromClassName : mStore.conversionMethods.keySet()) {
968974a07824642fabd896930f20c7b176fd2333e31George Mount                try {
969a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                    ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports);
970974a07824642fabd896930f20c7b176fd2333e31George Mount                    if (canUseForConversion(from, convertFrom)) {
971974a07824642fabd896930f20c7b176fd2333e31George Mount                        HashMap<String, MethodDescription> conversion =
972974a07824642fabd896930f20c7b176fd2333e31George Mount                                mStore.conversionMethods.get(fromClassName);
973974a07824642fabd896930f20c7b176fd2333e31George Mount                        for (String toClassName : conversion.keySet()) {
974974a07824642fabd896930f20c7b176fd2333e31George Mount                            try {
975a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                                ModelClass convertTo = mClassAnalyzer.findClass(toClassName,
976a7e767e576adebcddf043ad34ccb8dd167f777b3George Mount                                        imports);
977974a07824642fabd896930f20c7b176fd2333e31George Mount                                if (canUseForConversion(convertTo, to)) {
978974a07824642fabd896930f20c7b176fd2333e31George Mount                                    return conversion.get(toClassName);
979974a07824642fabd896930f20c7b176fd2333e31George Mount                                }
980974a07824642fabd896930f20c7b176fd2333e31George Mount                            } catch (Exception e) {
98197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                                L.d(e, "Unknown class: %s", toClassName);
982974a07824642fabd896930f20c7b176fd2333e31George Mount                            }
9830935fcaf455ce6fc6916194da651b556e1ec0326George Mount                        }
9840935fcaf455ce6fc6916194da651b556e1ec0326George Mount                    }
985974a07824642fabd896930f20c7b176fd2333e31George Mount                } catch (Exception e) {
98697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    L.d(e, "Unknown class: %s", fromClassName);
9873561e3e665698843b1c664385a842e779198960bGeorge Mount                }
9883561e3e665698843b1c664385a842e779198960bGeorge Mount            }
9893561e3e665698843b1c664385a842e779198960bGeorge Mount        }
9903561e3e665698843b1c664385a842e779198960bGeorge Mount        return null;
9913561e3e665698843b1c664385a842e779198960bGeorge Mount    }
9923561e3e665698843b1c664385a842e779198960bGeorge Mount
99379fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    private boolean canUseForConversion(ModelClass from, ModelClass to) {
994bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        if (from.isIncomplete() || to.isIncomplete()) {
995bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            from = from.erasure();
996bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            to = to.erasure();
997bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        }
998fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return from.equals(to) || ModelMethod.isBoxingConversion(from, to) ||
999fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                to.isAssignableFrom(from);
10003561e3e665698843b1c664385a842e779198960bGeorge Mount    }
10013561e3e665698843b1c664385a842e779198960bGeorge Mount
1002d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static void merge(IntermediateV2 store, Intermediate dumpStore) {
1003d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        IntermediateV2 intermediateV2 = (IntermediateV2) dumpStore.upgrade();
1004d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        merge(store.adapterMethods, intermediateV2.adapterMethods);
1005d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        merge(store.renamedMethods, intermediateV2.renamedMethods);
1006d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        merge(store.conversionMethods, intermediateV2.conversionMethods);
1007d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        store.multiValueAdapters.putAll(intermediateV2.multiValueAdapters);
1008d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        store.untaggableTypes.putAll(intermediateV2.untaggableTypes);
1009d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        merge(store.inverseAdapters, intermediateV2.inverseAdapters);
1010d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        merge(store.inverseMethods, intermediateV2.inverseMethods);
10113561e3e665698843b1c664385a842e779198960bGeorge Mount    }
10123561e3e665698843b1c664385a842e779198960bGeorge Mount
1013d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static <K, V, D> void merge(HashMap<K, HashMap<V, D>> first,
1014d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            HashMap<K, HashMap<V, D>> second) {
10153561e3e665698843b1c664385a842e779198960bGeorge Mount        for (K key : second.keySet()) {
1016d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            HashMap<V, D> firstVals = first.get(key);
1017d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            HashMap<V, D> secondVals = second.get(key);
10183561e3e665698843b1c664385a842e779198960bGeorge Mount            if (firstVals == null) {
10193561e3e665698843b1c664385a842e779198960bGeorge Mount                first.put(key, secondVals);
10203561e3e665698843b1c664385a842e779198960bGeorge Mount            } else {
10213561e3e665698843b1c664385a842e779198960bGeorge Mount                for (V key2 : secondVals.keySet()) {
10223561e3e665698843b1c664385a842e779198960bGeorge Mount                    if (!firstVals.containsKey(key2)) {
10233561e3e665698843b1c664385a842e779198960bGeorge Mount                        firstVals.put(key2, secondVals.get(key2));
10243561e3e665698843b1c664385a842e779198960bGeorge Mount                    }
10253561e3e665698843b1c664385a842e779198960bGeorge Mount                }
10263561e3e665698843b1c664385a842e779198960bGeorge Mount            }
10273561e3e665698843b1c664385a842e779198960bGeorge Mount        }
10283561e3e665698843b1c664385a842e779198960bGeorge Mount    }
10293561e3e665698843b1c664385a842e779198960bGeorge Mount
1030a128d1c99ea98bb48c45d648906652e3d618d513George Mount    private static String createAdapterCall(MethodDescription adapter,
1031e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            String componentExpression, String viewExpression, String... args) {
1032e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        StringBuilder sb = new StringBuilder();
1033e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1034e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        if (adapter.isStatic) {
1035e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            sb.append(adapter.type);
1036e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        } else {
1037a128d1c99ea98bb48c45d648906652e3d618d513George Mount            final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
1038a128d1c99ea98bb48c45d648906652e3d618d513George Mount            final String binderCall =  setterStore.getBindingAdapterCall(adapter.type);
1039a128d1c99ea98bb48c45d648906652e3d618d513George Mount            sb.append(componentExpression).append('.').append(binderCall);
1040e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
1041e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        sb.append('.').append(adapter.method).append('(');
1042e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        if (adapter.componentClass != null) {
1043e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            if (!"DataBindingComponent".equals(adapter.componentClass)) {
1044e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                sb.append('(').append(adapter.componentClass).append(") ");
1045e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            }
1046e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            sb.append(componentExpression).append(", ");
1047e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
1048e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        sb.append(viewExpression);
1049e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        for (String arg: args) {
1050e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            sb.append(", ").append(arg);
1051e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
1052e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        sb.append(')');
1053e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        return sb.toString();
1054e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount    }
1055e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1056cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    private static class MultiValueAdapterKey implements Serializable {
1057cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        private static final long serialVersionUID = 1;
1058cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1059cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final String viewType;
1060cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1061cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final String[] attributes;
1062cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1063cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final String[] parameterTypes;
1064cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
106596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount        public final boolean requireAll;
106696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount
1067cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>();
1068cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1069b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount        public MultiValueAdapterKey(ProcessingEnvironment processingEnv,
107096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                ExecutableElement method, String[] attributes, boolean takesComponent,
107196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                boolean requireAll) {
1072cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            this.attributes = stripAttributes(attributes);
107396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            this.requireAll = requireAll;
1074cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            List<? extends VariableElement> parameters = method.getParameters();
1075e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            final int argStart = 1 + (takesComponent ? 1 : 0);
1076e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.viewType = getQualifiedName(eraseType(processingEnv,
1077e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                    parameters.get(argStart - 1).asType()));
1078e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.parameterTypes = new String[parameters.size() - argStart];
107920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            for (int i = 0; i < attributes.length; i++) {
1080e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                TypeMirror typeMirror = eraseType(processingEnv,
1081e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                        parameters.get(i + argStart).asType());
1082b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount                this.parameterTypes[i] = getQualifiedName(typeMirror);
1083cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                attributeIndices.put(this.attributes[i], i);
1084cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
1085cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
1086cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1087cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        @Override
1088cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public boolean equals(Object obj) {
1089cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (!(obj instanceof MultiValueAdapterKey)) {
1090cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                return false;
1091cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
1092cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            final MultiValueAdapterKey that = (MultiValueAdapterKey) obj;
1093cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            if (!this.viewType.equals(that.viewType) ||
1094cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    this.attributes.length != that.attributes.length ||
1095cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) {
1096cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                return false;
1097cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
1098cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1099cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            for (int i = 0; i < this.attributes.length; i++) {
1100cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                final int thatIndex = that.attributeIndices.get(this.attributes[i]);
1101cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                final String thisParameter = parameterTypes[i];
1102cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                final String thatParameter = that.parameterTypes[thatIndex];
1103cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                if (!thisParameter.equals(thatParameter)) {
1104cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    return false;
1105cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                }
1106cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
1107cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            return true;
1108cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
1109cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1110cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        @Override
1111cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public int hashCode() {
11122611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            return mergedHashCode(viewType, attributeIndices.keySet());
1113cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
1114cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
1115cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
11162611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    private static int mergedHashCode(Object... objects) {
11172611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        return Arrays.hashCode(objects);
11182611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    }
11192611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar
11203561e3e665698843b1c664385a842e779198960bGeorge Mount    private static class MethodDescription implements Serializable {
11213561e3e665698843b1c664385a842e779198960bGeorge Mount
11223561e3e665698843b1c664385a842e779198960bGeorge Mount        private static final long serialVersionUID = 1;
11233561e3e665698843b1c664385a842e779198960bGeorge Mount
11243561e3e665698843b1c664385a842e779198960bGeorge Mount        public final String type;
11253561e3e665698843b1c664385a842e779198960bGeorge Mount
11263561e3e665698843b1c664385a842e779198960bGeorge Mount        public final String method;
11273561e3e665698843b1c664385a842e779198960bGeorge Mount
112820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        public final boolean requiresOldValue;
112920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
1130e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public final boolean isStatic;
1131e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1132e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public final String componentClass;
1133e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1134e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public MethodDescription(String type, String method) {
11353561e3e665698843b1c664385a842e779198960bGeorge Mount            this.type = type;
11363561e3e665698843b1c664385a842e779198960bGeorge Mount            this.method = method;
1137e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.requiresOldValue = false;
1138e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.isStatic = true;
1139e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.componentClass = null;
114097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            L.d("BINARY created method desc 1 %s %s", type, method );
11413561e3e665698843b1c664385a842e779198960bGeorge Mount        }
11423561e3e665698843b1c664385a842e779198960bGeorge Mount
1143e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public MethodDescription(ExecutableElement method, int numAttributes,
1144e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                boolean takesComponent) {
11453561e3e665698843b1c664385a842e779198960bGeorge Mount            TypeElement enclosingClass = (TypeElement) method.getEnclosingElement();
11463561e3e665698843b1c664385a842e779198960bGeorge Mount            this.type = enclosingClass.getQualifiedName().toString();
11473561e3e665698843b1c664385a842e779198960bGeorge Mount            this.method = method.getSimpleName().toString();
1148e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            final int argStart = 1 + (takesComponent ? 1 : 0);
1149e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2;
1150e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.isStatic = method.getModifiers().contains(Modifier.STATIC);
1151e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            this.componentClass = takesComponent
1152e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                    ? getQualifiedName(method.getParameters().get(0).asType())
1153e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                    : null;
1154e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
115597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method);
11563561e3e665698843b1c664385a842e779198960bGeorge Mount        }
11573561e3e665698843b1c664385a842e779198960bGeorge Mount
11583561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
11593561e3e665698843b1c664385a842e779198960bGeorge Mount        public boolean equals(Object obj) {
11603561e3e665698843b1c664385a842e779198960bGeorge Mount            if (obj instanceof MethodDescription) {
11613561e3e665698843b1c664385a842e779198960bGeorge Mount                MethodDescription that = (MethodDescription) obj;
11623561e3e665698843b1c664385a842e779198960bGeorge Mount                return that.type.equals(this.type) && that.method.equals(this.method);
11633561e3e665698843b1c664385a842e779198960bGeorge Mount            } else {
11643561e3e665698843b1c664385a842e779198960bGeorge Mount                return false;
11653561e3e665698843b1c664385a842e779198960bGeorge Mount            }
11663561e3e665698843b1c664385a842e779198960bGeorge Mount        }
11673561e3e665698843b1c664385a842e779198960bGeorge Mount
11683561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
11693561e3e665698843b1c664385a842e779198960bGeorge Mount        public int hashCode() {
11702611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            return mergedHashCode(type, method);
11713561e3e665698843b1c664385a842e779198960bGeorge Mount        }
11723561e3e665698843b1c664385a842e779198960bGeorge Mount
11733561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
11743561e3e665698843b1c664385a842e779198960bGeorge Mount        public String toString() {
11753561e3e665698843b1c664385a842e779198960bGeorge Mount            return type + "." + method + "()";
11763561e3e665698843b1c664385a842e779198960bGeorge Mount        }
11773561e3e665698843b1c664385a842e779198960bGeorge Mount    }
11783561e3e665698843b1c664385a842e779198960bGeorge Mount
1179d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static class InverseDescription extends MethodDescription {
1180d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private static final long serialVersionUID = 1;
1181d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1182d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public final String event;
1183d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1184d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public InverseDescription(String type, String method, String event) {
1185d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            super(type, method);
1186d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            this.event = event;
1187d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1188d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1189d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public InverseDescription(ExecutableElement method, String event, boolean takesComponent) {
1190d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            super(method, 1, takesComponent);
1191d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            this.event = event;
1192d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1193d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1194d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1195d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public boolean equals(Object obj) {
1196d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (!super.equals(obj) || !(obj instanceof InverseDescription)) {
1197d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                return false;
1198d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
1199d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return event.equals(((InverseDescription) obj).event);
1200d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1201d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1202d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1203d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public int hashCode() {
1204d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mergedHashCode(type, method, event);
1205d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1206d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1207d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
12083561e3e665698843b1c664385a842e779198960bGeorge Mount    private static class AccessorKey implements Serializable {
12093561e3e665698843b1c664385a842e779198960bGeorge Mount
12103561e3e665698843b1c664385a842e779198960bGeorge Mount        private static final long serialVersionUID = 1;
12113561e3e665698843b1c664385a842e779198960bGeorge Mount
12123561e3e665698843b1c664385a842e779198960bGeorge Mount        public final String viewType;
12133561e3e665698843b1c664385a842e779198960bGeorge Mount
12143561e3e665698843b1c664385a842e779198960bGeorge Mount        public final String valueType;
12153561e3e665698843b1c664385a842e779198960bGeorge Mount
12163561e3e665698843b1c664385a842e779198960bGeorge Mount        public AccessorKey(String viewType, String valueType) {
12173561e3e665698843b1c664385a842e779198960bGeorge Mount            this.viewType = viewType;
12183561e3e665698843b1c664385a842e779198960bGeorge Mount            this.valueType = valueType;
12193561e3e665698843b1c664385a842e779198960bGeorge Mount        }
12203561e3e665698843b1c664385a842e779198960bGeorge Mount
12213561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
12223561e3e665698843b1c664385a842e779198960bGeorge Mount        public int hashCode() {
12232611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            return mergedHashCode(viewType, valueType);
12243561e3e665698843b1c664385a842e779198960bGeorge Mount        }
12253561e3e665698843b1c664385a842e779198960bGeorge Mount
12263561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
12273561e3e665698843b1c664385a842e779198960bGeorge Mount        public boolean equals(Object obj) {
12283561e3e665698843b1c664385a842e779198960bGeorge Mount            if (obj instanceof AccessorKey) {
12293561e3e665698843b1c664385a842e779198960bGeorge Mount                AccessorKey that = (AccessorKey) obj;
12303561e3e665698843b1c664385a842e779198960bGeorge Mount                return viewType.equals(that.valueType) && valueType.equals(that.valueType);
12313561e3e665698843b1c664385a842e779198960bGeorge Mount            } else {
12323561e3e665698843b1c664385a842e779198960bGeorge Mount                return false;
12333561e3e665698843b1c664385a842e779198960bGeorge Mount            }
12343561e3e665698843b1c664385a842e779198960bGeorge Mount        }
12353561e3e665698843b1c664385a842e779198960bGeorge Mount
12363561e3e665698843b1c664385a842e779198960bGeorge Mount        @Override
12373561e3e665698843b1c664385a842e779198960bGeorge Mount        public String toString() {
12383561e3e665698843b1c664385a842e779198960bGeorge Mount            return "AK(" + viewType + ", " + valueType + ")";
12393561e3e665698843b1c664385a842e779198960bGeorge Mount        }
12403561e3e665698843b1c664385a842e779198960bGeorge Mount    }
12413561e3e665698843b1c664385a842e779198960bGeorge Mount
1242a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private interface Intermediate extends Serializable {
12434711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        Intermediate upgrade();
12443561e3e665698843b1c664385a842e779198960bGeorge Mount    }
12453561e3e665698843b1c664385a842e779198960bGeorge Mount
12464711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount    private static class IntermediateV1 implements Serializable, Intermediate {
12474711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        private static final long serialVersionUID = 1;
12484711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods =
1249895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar                new HashMap<String, HashMap<AccessorKey, MethodDescription>>();
12504711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods =
1251895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar                new HashMap<String, HashMap<String, MethodDescription>>();
12524711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods =
1253895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar                new HashMap<String, HashMap<String, MethodDescription>>();
1254895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        public final HashMap<String, String> untaggableTypes = new HashMap<String, String>();
1255cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters =
1256cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                new HashMap<MultiValueAdapterKey, MethodDescription>();
12573561e3e665698843b1c664385a842e779198960bGeorge Mount
12584711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        public IntermediateV1() {
12593561e3e665698843b1c664385a842e779198960bGeorge Mount        }
1260d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
1261d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        @Override
12624711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount        public Intermediate upgrade() {
1263d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            IntermediateV2 v2 = new IntermediateV2();
1264d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            v2.adapterMethods.putAll(adapterMethods);
1265d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            v2.renamedMethods.putAll(renamedMethods);
1266d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            v2.conversionMethods.putAll(conversionMethods);
1267d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            v2.untaggableTypes.putAll(untaggableTypes);
1268d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            v2.multiValueAdapters.putAll(multiValueAdapters);
1269d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return v2;
1270d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1271d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1272d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1273d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static class IntermediateV2 extends IntermediateV1 {
1274d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public final HashMap<String, HashMap<AccessorKey, InverseDescription>> inverseAdapters =
1275d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                new HashMap<String, HashMap<AccessorKey, InverseDescription>>();
1276d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public final HashMap<String, HashMap<String, InverseDescription>> inverseMethods =
1277d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                new HashMap<String, HashMap<String, InverseDescription>>();
1278d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1279d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1280d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public Intermediate upgrade() {
12814711e767639124a4e9720fb72f03d1175fa7312fGeorge Mount            return this;
12823561e3e665698843b1c664385a842e779198960bGeorge Mount        }
12833561e3e665698843b1c664385a842e779198960bGeorge Mount    }
128497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
128597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static class DummySetter extends SetterCall {
128697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private String mMethodName;
128797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
128897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public DummySetter(String methodName) {
128997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            mMethodName = methodName;
129097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
129197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
129297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1293e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String toJavaInternal(String componentExpression, String viewExpression,
1294e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String valueExpression) {
129597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return viewExpression + "." + mMethodName + "(" + valueExpression + ")";
129697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
129797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
129897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1299e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String toJavaInternal(String componentExpression, String viewExpression,
1300e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String oldValue, String valueExpression) {
130120c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return viewExpression + "." + mMethodName + "(" + valueExpression + ")";
130220c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
130320c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
130420c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
130597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public int getMinApi() {
130697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return 1;
130797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
130820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
130920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
131020c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        public boolean requiresOldValue() {
131120c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return false;
131220c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
1313716ba89e7f459f49ea85070d4710c1d79d715298George Mount
1314716ba89e7f459f49ea85070d4710c1d79d715298George Mount        @Override
1315716ba89e7f459f49ea85070d4710c1d79d715298George Mount        public ModelClass[] getParameterTypes() {
1316716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return new ModelClass[] {
1317716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    ModelAnalyzer.getInstance().findClass(Object.class)
1318716ba89e7f459f49ea85070d4710c1d79d715298George Mount            };
1319716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
1320e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1321e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        @Override
1322e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String getBindingAdapterInstanceClass() {
1323e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return null;
1324e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
132597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
132697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
132797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static class AdapterSetter extends SetterCall {
132897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        final MethodDescription mAdapter;
1329716ba89e7f459f49ea85070d4710c1d79d715298George Mount        final ModelClass mParameterType;
133097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
1331716ba89e7f459f49ea85070d4710c1d79d715298George Mount        public AdapterSetter(MethodDescription adapter, ModelClass parameterType) {
133297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            mAdapter = adapter;
1333716ba89e7f459f49ea85070d4710c1d79d715298George Mount            mParameterType = parameterType;
133497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
133597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
133697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1337e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String toJavaInternal(String componentExpression, String viewExpression,
1338e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String valueExpression) {
1339a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return createAdapterCall(mAdapter, componentExpression,
1340e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                    viewExpression, mCastString + valueExpression);
134197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
134297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
134397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1344e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        protected String toJavaInternal(String componentExpression, String viewExpression,
1345e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String oldValue, String valueExpression) {
1346a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return createAdapterCall(mAdapter, componentExpression,
1347e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                    viewExpression, mCastString + oldValue, mCastString + valueExpression);
134820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
134920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
135020c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
135197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public int getMinApi() {
135297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return 1;
135397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
135420c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
135520c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
135620c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        public boolean requiresOldValue() {
135720c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return mAdapter.requiresOldValue;
135820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
1359716ba89e7f459f49ea85070d4710c1d79d715298George Mount
1360716ba89e7f459f49ea85070d4710c1d79d715298George Mount        @Override
1361716ba89e7f459f49ea85070d4710c1d79d715298George Mount        public ModelClass[] getParameterTypes() {
1362716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return new ModelClass[] { mParameterType };
1363716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
1364e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1365e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        @Override
1366e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String getBindingAdapterInstanceClass() {
1367e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return mAdapter.isStatic ? null : mAdapter.type;
1368e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
136997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
137097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
137197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static class ModelMethodSetter extends SetterCall {
137297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        final ModelMethod mModelMethod;
137397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
137497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public ModelMethodSetter(ModelMethod modelMethod) {
137597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            mModelMethod = modelMethod;
137697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
137797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
137897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1379e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String toJavaInternal(String componentExpression, String viewExpression,
1380e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String valueExpression) {
1381fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            return viewExpression + "." + mModelMethod.getName() + "(" + mCastString +
1382fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    valueExpression + ")";
138397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
138497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
138597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        @Override
1386e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        protected String toJavaInternal(String componentExpression, String viewExpression,
1387e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String oldValue, String valueExpression) {
138820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return viewExpression + "." + mModelMethod.getName() + "(" +
138920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount                    mCastString + oldValue + ", " + mCastString + valueExpression + ")";
139020c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
139120c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
139220c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
139397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public int getMinApi() {
139497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return mModelMethod.getMinApi();
139597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
139620c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
139720c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
139820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        public boolean requiresOldValue() {
139920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return mModelMethod.getParameterTypes().length == 3;
140020c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
1401716ba89e7f459f49ea85070d4710c1d79d715298George Mount
1402716ba89e7f459f49ea85070d4710c1d79d715298George Mount        @Override
1403716ba89e7f459f49ea85070d4710c1d79d715298George Mount        public ModelClass[] getParameterTypes() {
1404716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return new ModelClass[] { mModelMethod.getParameterTypes()[0] };
1405716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
1406e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1407e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        @Override
1408e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String getBindingAdapterInstanceClass() {
1409e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return null;
1410e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
141197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
141297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
141320c7182163d99575d382e065f5a5fe45ed6b87e2George Mount    public interface BindingSetterCall {
1414e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        String toJava(String componentExpression, String viewExpression,
1415e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String... valueExpressions);
1416e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar
1417e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        int getMinApi();
141820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
141920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        boolean requiresOldValue();
1420716ba89e7f459f49ea85070d4710c1d79d715298George Mount
1421716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ModelClass[] getParameterTypes();
1422e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1423e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        String getBindingAdapterInstanceClass();
1424e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar    }
1425e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar
1426e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar    public static abstract class SetterCall implements BindingSetterCall {
142797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private MethodDescription mConverter;
1428fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        protected String mCastString = "";
142997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
143097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public SetterCall() {
143197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
143297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
143397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public void setConverter(MethodDescription converter) {
143497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            mConverter = converter;
143597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
143697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
1437e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        protected abstract String toJavaInternal(String componentExpression, String viewExpression,
143820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount                String converted);
143920c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
1440e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        protected abstract String toJavaInternal(String componentExpression, String viewExpression,
1441e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String oldValue, String converted);
1442e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1443e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        @Override
1444e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public final String toJava(String componentExpression, String viewExpression,
1445e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String... valueExpression) {
14462611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            Preconditions.check(valueExpression.length == 2, "value expressions size must be 2");
144720c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            if (requiresOldValue()) {
1448e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                return toJavaInternal(componentExpression, viewExpression,
1449e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                        convertValue(valueExpression[0]), convertValue(valueExpression[1]));
145020c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            } else {
1451e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                return toJavaInternal(componentExpression, viewExpression,
1452e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                        convertValue(valueExpression[1]));
145320c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            }
145497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
145597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
145697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        protected String convertValue(String valueExpression) {
145797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return mConverter == null ? valueExpression :
145897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    mConverter.type + "." + mConverter.method + "(" + valueExpression + ")";
145997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
146097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
146197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        abstract public int getMinApi();
1462fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
1463fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        public void setCast(ModelClass castTo) {
1464fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            mCastString = "(" + castTo.toJavaCode() + ") ";
1465fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
146697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
1467cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1468e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar    public static class MultiAttributeSetter implements BindingSetterCall {
1469cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        public final String[] attributes;
1470cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        private final MethodDescription mAdapter;
1471cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        private final MethodDescription[] mConverters;
1472cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        private final String[] mCasts;
1473cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        private final MultiValueAdapterKey mKey;
147496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount        private final boolean[] mSupplied;
1475cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
147696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount        public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied,
1477cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                MethodDescription adapter, MethodDescription[] converters, String[] casts) {
14782611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            Preconditions.check(converters != null &&
147996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    converters.length == key.attributes.length &&
148096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    casts != null && casts.length == key.attributes.length &&
148196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    supplied.length == key.attributes.length,
14822611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar                    "invalid arguments to create multi attr setter");
1483cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            this.mAdapter = adapter;
1484cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            this.mConverters = converters;
1485cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            this.mCasts = casts;
1486cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            this.mKey = key;
148796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            this.mSupplied = supplied;
148896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            if (key.requireAll) {
148996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                this.attributes = key.attributes;
149096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            } else {
149196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                int numSupplied = 0;
149296b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                for (int i = 0; i < mKey.attributes.length; i++) {
149396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    if (supplied[i]) {
149496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        numSupplied++;
149596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    }
149696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                }
149796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                if (numSupplied == key.attributes.length) {
149896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    this.attributes = key.attributes;
149996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                } else {
150096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    this.attributes = new String[numSupplied];
150196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    int attrIndex = 0;
150296b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    for (int i = 0; i < key.attributes.length; i++) {
150396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        if (supplied[i]) {
150496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                            attributes[attrIndex++] = key.attributes[i];
150596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        }
150696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    }
150796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                }
150896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            }
1509cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
1510cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount
1511e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        @Override
1512e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public final String toJava(String componentExpression, String viewExpression,
1513e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                String[] valueExpressions) {
15142611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar            Preconditions.check(valueExpressions.length == attributes.length * 2,
1515e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    "MultiAttributeSetter needs %s items, received %s",
1516e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    Arrays.toString(attributes), Arrays.toString(valueExpressions));
151796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            final int numAttrs = mKey.attributes.length;
1518e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)];
1519e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
152096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs;
152196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length;
152296b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
1523e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            StringBuilder argBuilder = new StringBuilder();
152496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            final int endIndex = numAttrs * 2;
152596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            for (int i = startIndex; i < endIndex; i++) {
1526e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount                argBuilder.setLength(0);
152796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                if (!mSupplied[i % numAttrs]) {
152896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    final String paramType = mKey.parameterTypes[i % numAttrs];
152996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    final String defaultValue = modelAnalyzer.getDefaultValue(paramType);
153096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    argBuilder.append('(')
153196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                            .append(paramType)
153296b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                            .append(')')
153396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                            .append(defaultValue);
1534cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                } else {
153596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    if (mConverters[i % numAttrs] != null) {
153696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        final MethodDescription converter = mConverters[i % numAttrs];
153796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        argBuilder.append(converter.type)
153896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                .append('.')
153996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                .append(converter.method)
154096b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                .append('(')
154196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                .append(valueExpressions[attrIndex])
1542cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                                .append(')');
154396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    } else {
154496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        if (mCasts[i % numAttrs] != null) {
154596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                            argBuilder.append('(')
154696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                    .append(mCasts[i % numAttrs])
154796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                                    .append(')');
154896b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        }
154996b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                        argBuilder.append(valueExpressions[attrIndex]);
1550cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                    }
155196b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    attrIndex++;
1552cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount                }
1553793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount                args[i - startIndex] = argBuilder.toString();
1554cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount            }
1555a128d1c99ea98bb48c45d648906652e3d618d513George Mount            return createAdapterCall(mAdapter, componentExpression, viewExpression, args);
1556cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount        }
1557e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar
1558e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        @Override
1559e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        public int getMinApi() {
1560e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar            return 1;
1561e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        }
1562e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar
1563e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        @Override
156420c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        public boolean requiresOldValue() {
156520c7182163d99575d382e065f5a5fe45ed6b87e2George Mount            return mAdapter.requiresOldValue;
156620c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        }
156720c7182163d99575d382e065f5a5fe45ed6b87e2George Mount
156820c7182163d99575d382e065f5a5fe45ed6b87e2George Mount        @Override
1569716ba89e7f459f49ea85070d4710c1d79d715298George Mount        public ModelClass[] getParameterTypes() {
1570716ba89e7f459f49ea85070d4710c1d79d715298George Mount            ModelClass[] parameters = new ModelClass[attributes.length];
1571716ba89e7f459f49ea85070d4710c1d79d715298George Mount            String[] paramTypeStrings = mKey.parameterTypes;
1572716ba89e7f459f49ea85070d4710c1d79d715298George Mount            ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
157396b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            int attrIndex = 0;
157496b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount            for (int i = 0; i < mKey.attributes.length; i++) {
157596b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                if (mSupplied[i]) {
157696b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                    parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null);
157796b22e7bbbf942aea1079dc8e8d0c4657663e5a7George Mount                }
1578716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
1579716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return parameters;
1580716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
1581716ba89e7f459f49ea85070d4710c1d79d715298George Mount
1582716ba89e7f459f49ea85070d4710c1d79d715298George Mount        @Override
1583e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        public String getBindingAdapterInstanceClass() {
1584e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount            return mAdapter.isStatic ? null : mAdapter.type;
1585e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        }
1586e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount
1587e4cd38824a6627b9fef229c549c636e35ad63b5fGeorge Mount        @Override
1588e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        public String toString() {
1589e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar            return "MultiAttributeSetter{" +
1590e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    "attributes=" + Arrays.toString(attributes) +
1591e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    ", mAdapter=" + mAdapter +
1592e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    ", mConverters=" + Arrays.toString(mConverters) +
1593e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    ", mCasts=" + Arrays.toString(mCasts) +
1594e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    ", mKey=" + mKey +
1595e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar                    '}';
1596e9b33bac04bb1ce1444d7f1744fcec1ecd3a57daYigit Boyar        }
1597cffffe30fe53455856d3d41724b9d5dd21aebf9aGeorge Mount    }
1598d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1599d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public static class ViewDataBindingEventSetter implements BindingSetterCall {
1600d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1601d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ViewDataBindingEventSetter() {
1602d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1603d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1604d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1605d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String toJava(String componentExpression, String viewExpression,
1606d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                String... valueExpressions) {
1607d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return "setBindingInverseListener(" + viewExpression + ", " +
1608d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    valueExpressions[0] + ", " + valueExpressions[1] + ")";
1609d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1610d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1611d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1612d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public int getMinApi() {
1613d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return 0;
1614d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1615d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1616d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1617d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public boolean requiresOldValue() {
1618d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return true;
1619d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1620d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1621d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1622d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ModelClass[] getParameterTypes() {
1623d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            ModelClass[] parameterTypes = new ModelClass[1];
1624d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            parameterTypes[0] = ModelAnalyzer.getInstance().findClass(
1625d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    "android.databinding.ViewDataBinder.PropertyChangedInverseListener", null);
1626d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return parameterTypes;
1627d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1628d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1629d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1630d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getBindingAdapterInstanceClass() {
1631d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return null;
1632d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1633d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1634d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1635d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public interface BindingGetterCall {
1636d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String toJava(String componentExpression, String viewExpression);
1637d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1638d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        int getMinApi();
1639d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1640d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String getBindingAdapterInstanceClass();
1641d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1642d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        void setBindingAdapterCall(String method);
1643d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1644d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        BindingSetterCall getEvent();
1645d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1646d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String getEventAttribute();
1647d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1648d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1649d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public static class ViewDataBindingGetterCall implements BindingGetterCall {
1650d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final String mGetter;
1651d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final BindingSetterCall mEventSetter;
1652d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final String mAttribute;
1653d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1654d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ViewDataBindingGetterCall(String attribute) {
1655d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            final int colonIndex = attribute.indexOf(':');
1656d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mAttribute = attribute.substring(colonIndex + 1);
1657d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mGetter = "get" + StringUtils.capitalize(mAttribute);
1658d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mEventSetter = new ViewDataBindingEventSetter();
1659d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1660d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1661d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1662d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String toJava(String componentExpression, String viewExpression) {
1663d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return viewExpression + "." + mGetter + "()";
1664d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1665d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1666d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1667d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public int getMinApi() {
1668d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return 0;
1669d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1670d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1671d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1672d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getBindingAdapterInstanceClass() {
1673d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return null;
1674d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1675d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1676d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1677d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public void setBindingAdapterCall(String method) {
1678d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1679d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1680d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1681d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public BindingSetterCall getEvent() {
1682d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mEventSetter;
1683d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1684d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1685d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1686d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getEventAttribute() {
1687d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mAttribute;
1688d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1689d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1690d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1691d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public static class ViewGetterCall implements BindingGetterCall {
1692d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final InverseDescription mInverseDescription;
1693d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final BindingSetterCall mEventCall;
1694d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final ModelMethod mMethod;
1695d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1696d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ViewGetterCall(InverseDescription inverseDescription, ModelMethod method,
1697d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                BindingSetterCall eventCall) {
1698d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mInverseDescription = inverseDescription;
1699d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mEventCall = eventCall;
1700d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mMethod = method;
1701d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1702d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1703d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1704d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public BindingSetterCall getEvent() {
1705d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mEventCall;
1706d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1707d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1708d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1709d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getEventAttribute() {
1710d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mInverseDescription.event;
1711d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1712d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1713d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1714d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String toJava(String componentExpression, String viewExpression) {
1715d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return viewExpression + "." + mMethod.getName() + "()";
1716d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1717d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1718d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1719d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public int getMinApi() {
1720d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mMethod.getMinApi();
1721d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1722d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1723d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1724d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getBindingAdapterInstanceClass() {
1725d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return null;
1726d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1727d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1728d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1729d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public void setBindingAdapterCall(String method) {
1730d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1731d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1732d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1733d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public static class AdapterGetter implements BindingGetterCall {
1734d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final InverseDescription mInverseDescription;
1735d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private String mBindingAdapterCall;
1736d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        private final BindingSetterCall mEventCall;
1737d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1738d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public AdapterGetter(InverseDescription description, BindingSetterCall eventCall) {
1739d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mInverseDescription = description;
1740d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mEventCall = eventCall;
1741d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1742d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1743d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1744d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String toJava(String componentExpression, String viewExpression) {
1745d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            StringBuilder sb = new StringBuilder();
1746d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1747d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (mInverseDescription.isStatic) {
1748d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                sb.append(mInverseDescription.type);
1749d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            } else {
1750d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                sb.append(componentExpression).append('.').append(mBindingAdapterCall);
1751d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
1752d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            sb.append('.').append(mInverseDescription.method).append('(');
1753d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (mInverseDescription.componentClass != null) {
1754d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                if (!"DataBindingComponent".equals(mInverseDescription.componentClass)) {
1755d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    sb.append('(').append(mInverseDescription.componentClass).append(") ");
1756d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
1757d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                sb.append(componentExpression).append(", ");
1758d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
1759d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            sb.append(viewExpression).append(')');
1760d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return sb.toString();
1761d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1762d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1763d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1764d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public int getMinApi() {
1765d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return 1;
1766d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1767d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1768d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1769d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getBindingAdapterInstanceClass() {
1770d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mInverseDescription.isStatic ? null : mInverseDescription.type;
1771d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1772d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1773d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1774d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public void setBindingAdapterCall(String method) {
1775d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mBindingAdapterCall = method;
1776d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1777d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1778d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1779d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public BindingSetterCall getEvent() {
1780d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mEventCall;
1781d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1782d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1783d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        @Override
1784d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public String getEventAttribute() {
1785d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return mInverseDescription.event;
1786d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1787d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1788d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1789d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private static class InverseMethod {
1790d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public BindingGetterCall call;
1791d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ModelClass returnType;
1792d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public ModelClass viewType;
1793d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
1794d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        public InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType) {
1795d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            this.call = call;
1796d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            this.returnType = returnType;
1797d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            this.viewType = viewType;
1798d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
1799d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
18003561e3e665698843b1c664385a842e779198960bGeorge Mount}
1801