[go: nahoru, domu]

11b9940e612fc73202837fbe9db2f9035f307b5d1George Mount/*
21b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * Copyright (C) 2015 The Android Open Source Project
31b9940e612fc73202837fbe9db2f9035f307b5d1George Mount *
41b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * Licensed under the Apache License, Version 2.0 (the "License");
51b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * you may not use this file except in compliance with the License.
61b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * You may obtain a copy of the License at
71b9940e612fc73202837fbe9db2f9035f307b5d1George Mount *
81b9940e612fc73202837fbe9db2f9035f307b5d1George Mount *      http://www.apache.org/licenses/LICENSE-2.0
91b9940e612fc73202837fbe9db2f9035f307b5d1George Mount *
101b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * Unless required by applicable law or agreed to in writing, software
111b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * distributed under the License is distributed on an "AS IS" BASIS,
121b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * See the License for the specific language governing permissions and
141b9940e612fc73202837fbe9db2f9035f307b5d1George Mount * limitations under the License.
151b9940e612fc73202837fbe9db2f9035f307b5d1George Mount */
16fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.reflection;
171b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
188ec9696461abb5ad0b95024edc3f3ccb9ace85beGeorge Mountimport android.databinding.tool.reflection.Callable.Type;
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
204ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport android.databinding.tool.util.StringUtils;
211b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
22fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mountimport java.util.ArrayList;
23716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport java.util.Arrays;
24fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mountimport java.util.List;
251b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
26716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport static android.databinding.tool.reflection.Callable.CAN_BE_INVALIDATED;
27716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport static android.databinding.tool.reflection.Callable.DYNAMIC;
28716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport static android.databinding.tool.reflection.Callable.STATIC;
29716ba89e7f459f49ea85070d4710c1d79d715298George Mount
30fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mountpublic abstract class ModelClass {
31fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract String toJavaCode();
321b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
33fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
34fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether this ModelClass represents an array.
35fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
36fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isArray();
371b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
38fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
39fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * For arrays, lists, and maps, this returns the contained value. For other types, null
40fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * is returned.
41fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
42fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return The component type for arrays, the value type for maps, and the element type
43fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * for lists.
44fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
45fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract ModelClass getComponentType();
461b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
47fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
48fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return Whether or not this ModelClass can be treated as a List. This means
49fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * it is a java.util.List, or one of the Sparse*Array classes.
50fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
51fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isList() {
52fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (ModelClass listType : ModelAnalyzer.getInstance().getListTypes()) {
53fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (listType != null) {
54fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                if (listType.isAssignableFrom(this)) {
55fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    return true;
56fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                }
57fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
58fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
59fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return false;
60fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
611b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
62fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
63fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass can be considered a Map or not.
64fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
65fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isMap()  {
66fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return ModelAnalyzer.getInstance().getMapType().isAssignableFrom(erasure());
67fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
681b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
69fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
70fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass is a java.lang.String.
71fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
72fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isString() {
73fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return ModelAnalyzer.getInstance().getStringType().equals(this);
74fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
751b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
76fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
77fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Reference type.
78fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
79fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isNullable();
80fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
81fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
82fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a primitive type.
83fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
84fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isPrimitive();
851b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
86fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
87fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java boolean
88fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
89fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isBoolean();
90fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
91fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
92fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java char
93fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
94fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isChar();
95fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
96fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
97fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java byte
98fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
99fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isByte();
100fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
101fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
102fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java short
103fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
104fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isShort();
105fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
106fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
107fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java int
108fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
109fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isInt();
110fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
111fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
112fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java long
113fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
114fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isLong();
115fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
116fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
117fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java float
118fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
119fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isFloat();
120fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
121fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
122fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a Java double
123fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
124fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isDouble();
125fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
126fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
12791d538470c011e19fa4375cc3531b5dd9ae01d55George Mount     * @return whether or not this has type parameters
12891d538470c011e19fa4375cc3531b5dd9ae01d55George Mount     */
12991d538470c011e19fa4375cc3531b5dd9ae01d55George Mount    public abstract boolean isGeneric();
13091d538470c011e19fa4375cc3531b5dd9ae01d55George Mount
13191d538470c011e19fa4375cc3531b5dd9ae01d55George Mount    /**
132b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     * @return a list of Generic type paramters for the class. For example, if the class
133b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     * is List<T>, then the return value will be a list containing T. null is returned
134b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     * if this is not a generic type
135b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     */
136b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    public abstract List<ModelClass> getTypeArguments();
137b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
138b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    /**
139b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     * @return whether this is a type variable. For example, in List&lt;T>, T is a type variable.
140b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     * However, List&lt;String>, String is not a type variable.
141b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount     */
142b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    public abstract boolean isTypeVar();
143b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount
144b9e4aa96812692a7dcf468445e64bc5b30d3c79aGeorge Mount    /**
145bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount     * @return whether this is a wildcard type argument or not.
146bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount     */
147bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    public abstract boolean isWildcard();
148bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount
149bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    /**
150fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass.
151fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
152fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isObject() {
153fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return ModelAnalyzer.getInstance().getObjectType().equals(this);
154fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
1551b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
156fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
157716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @return whether or not this ModelClass is an interface
158716ba89e7f459f49ea85070d4710c1d79d715298George Mount     */
159716ba89e7f459f49ea85070d4710c1d79d715298George Mount    public abstract boolean isInterface();
160716ba89e7f459f49ea85070d4710c1d79d715298George Mount
161716ba89e7f459f49ea85070d4710c1d79d715298George Mount    /**
1625f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount     * @return whether or not his is a ViewDataBinding subclass.
1635f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount     */
1645f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount    public boolean isViewDataBinding() {
1655f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount        return ModelAnalyzer.getInstance().getViewDataBindingType().isAssignableFrom(this);
1665f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount    }
1675f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount
1685f3aae011cc291c2abbb90215c2e6f89a5f2626dGeorge Mount    /**
169de38dd3ef0577d25b2d59863603abe5750d0c231George Mount     * @return whether or not this ModelClass type extends ViewStub.
170de38dd3ef0577d25b2d59863603abe5750d0c231George Mount     */
171de38dd3ef0577d25b2d59863603abe5750d0c231George Mount    public boolean extendsViewStub() {
172de38dd3ef0577d25b2d59863603abe5750d0c231George Mount        return ModelAnalyzer.getInstance().getViewStubType().isAssignableFrom(this);
173de38dd3ef0577d25b2d59863603abe5750d0c231George Mount    }
174de38dd3ef0577d25b2d59863603abe5750d0c231George Mount
175de38dd3ef0577d25b2d59863603abe5750d0c231George Mount    /**
176fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this is an Observable type such as ObservableMap, ObservableList,
177fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * or Observable.
178fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
179fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isObservable() {
180fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
181fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return modelAnalyzer.getObservableType().isAssignableFrom(this) ||
182fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                modelAnalyzer.getObservableListType().isAssignableFrom(this) ||
183fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                modelAnalyzer.getObservableMapType().isAssignableFrom(this);
1841b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
185fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
1861b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
187fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
188fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this is an ObservableField, or any of the primitive versions
189fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * such as ObservableBoolean and ObservableInt
190fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
191fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public boolean isObservableField() {
192fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelClass erasure = erasure();
193fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (ModelClass observableField : ModelAnalyzer.getInstance().getObservableFieldTypes()) {
194fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (observableField.isAssignableFrom(erasure)) {
195fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                return true;
196fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
197fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
198fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return false;
199fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
2001b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
201fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
202fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return whether or not this ModelClass represents a void
203fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
204fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isVoid();
2051b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
206fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
207fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * When this is a boxed type, such as Integer, this will return the unboxed value,
208fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * such as int. If this is not a boxed type, this is returned.
209fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
210fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return The unboxed type of the class that this ModelClass represents or this if it isn't a
211fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * boxed type.
212fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
213fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract ModelClass unbox();
2141b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
215fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
216fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * When this is a primitive type, such as boolean, this will return the boxed value,
217fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * such as Boolean. If this is not a primitive type, this is returned.
218fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
219fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return The boxed type of the class that this ModelClass represents or this if it isn't a
220fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * primitive type.
221fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
222fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract ModelClass box();
2231b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
224fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
225fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns whether or not the type associated with <code>that</code> can be assigned to
226fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * the type associated with this ModelClass. If this and that only require boxing or unboxing
227fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * then true is returned.
228fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
229fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param that the ModelClass to compare.
230fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return true if <code>that</code> requires only boxing or if <code>that</code> is an
231fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * implementation of or subclass of <code>this</code>.
232fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
233fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract boolean isAssignableFrom(ModelClass that);
2341b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
235fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
236fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns an array containing all public methods on the type represented by this ModelClass
237fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * with the name <code>name</code> and can take the passed-in types as arguments. This will
238fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * also work if the arguments match VarArgs parameter.
239fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
240fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param name The name of the method to find.
241fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param args The types that the method should accept.
242ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     * @param staticOnly Whether only static methods should be returned or both instance methods
243ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     *                 and static methods are valid.
244ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     *
245fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return An array containing all public methods with the name <code>name</code> and taking
246fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * <code>args</code> parameters.
247fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
248ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar    public ModelMethod[] getMethods(String name, List<ModelClass> args, boolean staticOnly) {
249fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelMethod[] methods = getDeclaredMethods();
250fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
251fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (ModelMethod method : methods) {
252ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            if (method.isPublic() && (!staticOnly || method.isStatic()) &&
253fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    name.equals(method.getName()) && method.acceptsArguments(args)) {
254fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                matching.add(method);
255fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
256fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
257fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return matching.toArray(new ModelMethod[matching.size()]);
258fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
2591b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
260fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
261fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns all public instance methods with the given name and number of parameters.
262fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
263fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param name The name of the method to find.
264fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param numParameters The number of parameters that the method should take
265fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return An array containing all public methods with the given name and number of parameters.
266fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
267fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public ModelMethod[] getMethods(String name, int numParameters) {
268fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelMethod[] methods = getDeclaredMethods();
269fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
270fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (ModelMethod method : methods) {
271fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (method.isPublic() && !method.isStatic() &&
272fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    name.equals(method.getName()) &&
273fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    method.getParameterTypes().length == numParameters) {
274fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                matching.add(method);
275fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
276fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
277fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return matching.toArray(new ModelMethod[matching.size()]);
278fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
2791b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
280fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
281fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns the public method with the name <code>name</code> with the parameters that
282ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     * best match args. <code>staticOnly</code> governs whether a static or instance method
283fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * will be returned. If no matching method was found, null is returned.
284fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
285fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param name The method name to find
286fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param args The arguments that the method should accept
287ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     * @param staticOnly true if the returned method must be static or false if it does not
288ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     *                     matter.
289fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
290ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar    public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticOnly) {
291ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        ModelMethod[] methods = getMethods(name, args, staticOnly);
292ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        L.d("looking methods for %s. static only ? %s . method count: %d", name, staticOnly,
293ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                methods.length);
294ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        for (ModelMethod method : methods) {
295ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            L.d("method: %s, %s", method.getName(), method.isStatic());
296ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        }
297fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        if (methods.length == 0) {
298fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            return null;
299fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
300fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelMethod bestMethod = methods[0];
301fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (int i = 1; i < methods.length; i++) {
302fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (methods[i].isBetterArgMatchThan(bestMethod, args)) {
303fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                bestMethod = methods[i];
304fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
305fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
306fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return bestMethod;
307fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
3081b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
309fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
310fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * If this represents a class, the super class that it extends is returned. If this
311fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * represents an interface, the interface that this extends is returned.
312fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * <code>null</code> is returned if this is not a class or interface, such as an int, or
313fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * if it is java.lang.Object or an interface that does not extend any other type.
314fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *
315fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return The class or interface that this ModelClass extends or null.
316fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
317fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract ModelClass getSuperclass();
3181b9940e612fc73202837fbe9db2f9035f307b5d1George Mount
319fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
320fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return A String representation of the class or interface that this represents, not
321fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * including any type arguments.
322fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
323fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public String getCanonicalName() {
324fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return erasure().toJavaCode();
325fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
32697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
327fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
328793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount     * @return The class or interface name of this type or the primitive type if it isn't a
329793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount     * reference type.
330793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount     */
331793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount    public String getSimpleName() {
332793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        final String canonicalName = getCanonicalName();
333793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        final int dotIndex = canonicalName.lastIndexOf('.');
334793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        if (dotIndex >= 0) {
335793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount            return canonicalName.substring(dotIndex + 1);
336793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        }
337793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount        return canonicalName;
338793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount    }
339793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount
340793e979f25e190162eacf46d6a4efc3efc1d2f91George Mount    /**
341fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns this class type without any generic type arguments.
342fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @return this class type without any generic type arguments.
343fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
344fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract ModelClass erasure();
34597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
34697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    /**
34797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     * Since when this class is available. Important for Binding expressions so that we don't
34897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     * call non-existing APIs when setting UI.
34997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     *
35097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     * @return The SDK_INT where this method was added. If it is not a framework method, should
35197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     * return 1.
35297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     */
353fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public int getMinApi() {
354fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return SdkUtil.getMinApi(this);
355fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
35697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
35797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    /**
35897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     * Returns the JNI description of the method which can be used to lookup it in SDK.
359fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount     * @see TypeUtil
36097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar     */
361fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    public abstract String getJniDescription();
362fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
363fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    /**
364716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * Returns a list of all abstract methods in the type.
365716ba89e7f459f49ea85070d4710c1d79d715298George Mount     */
366716ba89e7f459f49ea85070d4710c1d79d715298George Mount    public List<ModelMethod> getAbstractMethods() {
367716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ArrayList<ModelMethod> abstractMethods = new ArrayList<ModelMethod>();
368716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ModelMethod[] methods = getDeclaredMethods();
369716ba89e7f459f49ea85070d4710c1d79d715298George Mount        for (ModelMethod method : methods) {
370716ba89e7f459f49ea85070d4710c1d79d715298George Mount            if (method.isAbstract()) {
371716ba89e7f459f49ea85070d4710c1d79d715298George Mount                abstractMethods.add(method);
372716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
373716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
374716ba89e7f459f49ea85070d4710c1d79d715298George Mount        return abstractMethods;
375716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
376716ba89e7f459f49ea85070d4710c1d79d715298George Mount
377716ba89e7f459f49ea85070d4710c1d79d715298George Mount    /**
378fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * Returns the getter method or field that the name refers to.
379fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     * @param name The name of the field or the body of the method name -- can be name(),
380fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *             getName(), or isName().
381ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar     * @param staticOnly Whether this should look for static methods and fields or instance
382fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     *                     versions
383716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @return the getter method or field that the name refers to or null if none can be found.
384fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount     */
385ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar    public Callable findGetterOrField(String name, boolean staticOnly) {
3868ec9696461abb5ad0b95024edc3f3ccb9ace85beGeorge Mount        if ("length".equals(name) && isArray()) {
387d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            return new Callable(Type.FIELD, name, null,
388d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0);
3898ec9696461abb5ad0b95024edc3f3ccb9ace85beGeorge Mount        }
390fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        String capitalized = StringUtils.capitalize(name);
391fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        String[] methodNames = {
392fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                "get" + capitalized,
393fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                "is" + capitalized,
394fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                name
395fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        };
396fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (String methodName : methodNames) {
397ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            ModelMethod[] methods = getMethods(methodName, new ArrayList<ModelClass>(), staticOnly);
398fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            for (ModelMethod method : methods) {
399716ba89e7f459f49ea85070d4710c1d79d715298George Mount                if (method.isPublic() && (!staticOnly || method.isStatic()) &&
400716ba89e7f459f49ea85070d4710c1d79d715298George Mount                        !method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
401019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                    int flags = DYNAMIC;
402019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                    if (method.isStatic()) {
403019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                        flags |= STATIC;
404019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                    }
405ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                    if (method.isBindable()) {
406019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                        flags |= CAN_BE_INVALIDATED;
407ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                    } else {
408ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        // if method is not bindable, look for a backing field
409ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        final ModelField backingField = getField(name, true, method.isStatic());
410ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        L.d("backing field for method %s is %s", method.getName(),
411ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                                backingField == null ? "NOT FOUND" : backingField.getName());
412ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        if (backingField != null && backingField.isBindable()) {
413ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                            flags |= CAN_BE_INVALIDATED;
414ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        }
415019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                    }
416d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    final ModelMethod setterMethod = findSetter(method, name);
417d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    final String setterName = setterMethod == null ? null : setterMethod.getName();
418fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    final Callable result = new Callable(Callable.Type.METHOD, methodName,
419d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            setterName, method.getReturnType(null), method.getParameterTypes().length,
42088ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar                            flags);
421fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    return result;
422fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                }
423fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
424fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
425fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
426ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        // could not find a method. Look for a public field
427ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        ModelField publicField = null;
428ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        if (staticOnly) {
429ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            publicField = getField(name, false, true);
430ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        } else {
431ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            // first check non-static
432ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            publicField = getField(name, false, false);
433ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            if (publicField == null) {
434ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // check for static
435ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                publicField = getField(name, false, true);
436019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            }
437019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar        }
438716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (publicField == null) {
439716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return null;
440716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
441ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        ModelClass fieldType = publicField.getFieldType();
442ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        int flags = 0;
443d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String setterFieldName = name;
444d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (publicField.isStatic()) {
445d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            flags |= STATIC;
446d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
447ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        if (!publicField.isFinal()) {
448d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            setterFieldName = null;
449ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            flags |= DYNAMIC;
450fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
451ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        if (publicField.isBindable()) {
452ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            flags |= CAN_BE_INVALIDATED;
453ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        }
454d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return new Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags);
455d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
456d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
457d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public ModelMethod findInstanceGetter(String name) {
458d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String capitalized = StringUtils.capitalize(name);
459d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        String[] methodNames = {
460d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                "get" + capitalized,
461d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                "is" + capitalized,
462d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                name
463d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        };
464d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (String methodName : methodNames) {
465d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            ModelMethod[] methods = getMethods(methodName, new ArrayList<ModelClass>(), false);
466d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            for (ModelMethod method : methods) {
467d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                if (method.isPublic() && !method.isStatic() &&
468d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        !method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
469d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    return method;
470d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
471d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
472ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar        }
473d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return null;
474fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
475fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
476ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar    private ModelField getField(String name, boolean allowPrivate, boolean isStatic) {
477fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        ModelField[] fields = getDeclaredFields();
478fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        for (ModelField field : fields) {
4790d6e2b8ac5e9e8635adf95c4166dd26441c51997Yigit Boyar            boolean nameMatch = name.equals(field.getName()) ||
4800d6e2b8ac5e9e8635adf95c4166dd26441c51997Yigit Boyar                    name.equals(stripFieldName(field.getName()));
4810d6e2b8ac5e9e8635adf95c4166dd26441c51997Yigit Boyar            if (nameMatch && field.isStatic() == isStatic &&
482019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                    (allowPrivate || field.isPublic())) {
483fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                return field;
484fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
485fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
486fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return null;
487fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
488fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
489d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    private ModelMethod findSetter(ModelMethod getter, String originalName) {
490d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final String capitalized = StringUtils.capitalize(originalName);
491d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final String[] possibleNames;
492d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        if (originalName.equals(getter.getName())) {
493d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            possibleNames = new String[] { originalName, "set" + capitalized };
494d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        } else if (getter.getName().startsWith("is")){
495d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            possibleNames = new String[] { "set" + capitalized, "setIs" + capitalized };
496d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        } else {
497d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            possibleNames = new String[] { "set" + capitalized };
498d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
499d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        for (String name : possibleNames) {
500d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            List<ModelMethod> methods = findMethods(name, getter.isStatic());
501d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            if (methods != null) {
502d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                ModelClass param = getter.getReturnType(null);
503d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                for (ModelMethod method : methods) {
504d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    ModelClass[] parameterTypes = method.getParameterTypes();
505d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    if (parameterTypes != null && parameterTypes.length == 1 &&
506d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            parameterTypes[0].equals(param) &&
507d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                            method.isStatic() == getter.isStatic()) {
508d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                        return method;
509d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                    }
510d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount                }
511d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            }
512d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        }
513d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return null;
514d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
515d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
516716ba89e7f459f49ea85070d4710c1d79d715298George Mount    /**
517716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * Finds public methods that matches the given name exactly. These may be resolved into
518716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * listener methods during Expr.resolveListeners.
519716ba89e7f459f49ea85070d4710c1d79d715298George Mount     */
520716ba89e7f459f49ea85070d4710c1d79d715298George Mount    public List<ModelMethod> findMethods(String name, boolean staticOnly) {
521716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ModelMethod[] methods = getDeclaredMethods();
522716ba89e7f459f49ea85070d4710c1d79d715298George Mount        ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
523716ba89e7f459f49ea85070d4710c1d79d715298George Mount        for (ModelMethod method : methods) {
524716ba89e7f459f49ea85070d4710c1d79d715298George Mount            if (method.getName().equals(name) && (!staticOnly || method.isStatic()) &&
525716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    method.isPublic()) {
526716ba89e7f459f49ea85070d4710c1d79d715298George Mount                matching.add(method);
527716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
528716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
529716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (matching.isEmpty()) {
530716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return null;
531716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
532716ba89e7f459f49ea85070d4710c1d79d715298George Mount        return matching;
533716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
534716ba89e7f459f49ea85070d4710c1d79d715298George Mount
535bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    public boolean isIncomplete() {
536bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        if (isTypeVar() || isWildcard()) {
537bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            return true;
538bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        }
539bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        List<ModelClass> typeArgs = getTypeArguments();
540bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        if (typeArgs != null) {
541bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            for (ModelClass typeArg : typeArgs) {
542bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                if (typeArg.isIncomplete()) {
543bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                    return true;
544bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount                }
545bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount            }
546bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        }
547bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount        return false;
548bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount    }
549bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128George Mount
550fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    protected abstract ModelField[] getDeclaredFields();
551fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
552fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    protected abstract ModelMethod[] getDeclaredMethods();
553fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount
554fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    private static String stripFieldName(String fieldName) {
555fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        // TODO: Make this configurable through IntelliJ
556fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        if (fieldName.length() > 2) {
557fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            final char start = fieldName.charAt(2);
558fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
559fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                return Character.toLowerCase(start) + fieldName.substring(3);
560fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
561fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
562fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        if (fieldName.length() > 1) {
563fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            final char start = fieldName.charAt(1);
564fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            final char fieldIdentifier = fieldName.charAt(0);
565fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            final boolean strip;
566fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (fieldIdentifier == '_') {
567fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                strip = true;
568fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            } else if (fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
569fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                    !Character.isLowerCase(start)) {
570fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                strip = true;
571fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            } else {
572fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                strip = false; // not mUppercase format
573fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
574fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (strip) {
575fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                return Character.toLowerCase(start) + fieldName.substring(2);
576fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
577fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        }
578fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount        return fieldName;
579fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount    }
5801b9940e612fc73202837fbe9db2f9035f307b5d1George Mount}
581