ModelMethod.java revision fead9ca09b117136b35bc5bf137340a754f9eddd
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.databinding.tool.reflection;
17
18import android.databinding.Bindable;
19
20import java.util.List;
21
22public abstract class ModelMethod {
23    public abstract ModelClass getDeclaringClass();
24
25    public abstract ModelClass[] getParameterTypes();
26
27    public abstract String getName();
28
29    public abstract ModelClass getReturnType(List<ModelClass> args);
30
31    public abstract boolean isVoid();
32
33    public abstract boolean isPublic();
34
35    public abstract boolean isStatic();
36
37    /**
38     * @return whether or not this method has been given the {@link Bindable} annotation.
39     */
40    public abstract boolean isBindable();
41
42    /**
43     * Since when this method is available. Important for Binding expressions so that we don't
44     * call non-existing APIs when setting UI.
45     *
46     * @return The SDK_INT where this method was added. If it is not a framework method, should
47     * return 1.
48     */
49    public abstract int getMinApi();
50
51    /**
52     * Returns the JNI description of the method which can be used to lookup it in SDK.
53     * @see TypeUtil
54     */
55    public abstract String getJniDescription();
56
57    /**
58     * @return true if the final parameter is a varargs parameter.
59     */
60    public abstract boolean isVarArgs();
61
62    /**
63     * @param args The arguments to the method
64     * @return Whether the arguments would be accepted as parameters to this method.
65     */
66    public boolean acceptsArguments(List<ModelClass> args) {
67        boolean isVarArgs = isVarArgs();
68        ModelClass[] parameterTypes = getParameterTypes();
69        if ((!isVarArgs && args.size() != parameterTypes.length) ||
70                (isVarArgs && args.size() < parameterTypes.length - 1)) {
71            return false; // The wrong number of parameters
72        }
73        boolean parametersMatch = true;
74        for (int i = 0; i < args.size(); i++) {
75            ModelClass parameterType = getParameter(i, parameterTypes);
76            ModelClass arg = args.get(i);
77            if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) {
78                parametersMatch = false;
79                break;
80            }
81        }
82        return parametersMatch;
83    }
84
85    public boolean isBetterArgMatchThan(ModelMethod other, List<ModelClass> args) {
86        final ModelClass[] parameterTypes = getParameterTypes();
87        final ModelClass[] otherParameterTypes = other.getParameterTypes();
88        for (int i = 0; i < args.size(); i++) {
89            final ModelClass arg = args.get(i);
90            final ModelClass thisParameter = getParameter(i, parameterTypes);
91            final ModelClass thatParameter = other.getParameter(i, otherParameterTypes);
92            final int diff = compareParameter(arg, thisParameter, thatParameter);
93            if (diff != 0) {
94                return diff < 0;
95            }
96        }
97        return false;
98    }
99
100    private ModelClass getParameter(int index, ModelClass[] parameterTypes) {
101        int normalParamCount = isVarArgs() ? parameterTypes.length - 1 : parameterTypes.length;
102        if (index < normalParamCount) {
103            return parameterTypes[index];
104        } else {
105            return parameterTypes[parameterTypes.length - 1].getComponentType();
106        }
107    }
108
109    private static int compareParameter(ModelClass arg, ModelClass thisParameter,
110            ModelClass thatParameter) {
111        if (thatParameter.equals(arg)) {
112            return 1;
113        } else if (thisParameter.equals(arg)) {
114            return -1;
115        } else if (isBoxingConversion(thatParameter, arg)) {
116            return 1;
117        } else if (isBoxingConversion(thisParameter, arg)) {
118            // Boxing/unboxing is second best
119            return -1;
120        } else {
121            int argConversionLevel = getImplicitConversionLevel(arg);
122            if (argConversionLevel != -1) {
123                int oldConversionLevel = getImplicitConversionLevel(thatParameter);
124                int newConversionLevel = getImplicitConversionLevel(thisParameter);
125                if (newConversionLevel != -1 &&
126                        (oldConversionLevel == -1 || newConversionLevel < oldConversionLevel)) {
127                    return -1;
128                } else if (oldConversionLevel != -1) {
129                    return 1;
130                }
131            }
132            // Look for more exact match
133            if (thatParameter.isAssignableFrom(thisParameter)) {
134                return -1;
135            }
136        }
137        return 0; // no difference
138    }
139
140    public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) {
141        if (class1.isPrimitive() != class2.isPrimitive()) {
142            return (class1.box().equals(class2.box()));
143        } else {
144            return false;
145        }
146    }
147
148    public static int getImplicitConversionLevel(ModelClass primitive) {
149        if (primitive == null) {
150            return -1;
151        } else if (primitive.isByte()) {
152            return 0;
153        } else if (primitive.isChar()) {
154            return 1;
155        } else if (primitive.isShort()) {
156            return 2;
157        } else if (primitive.isInt()) {
158            return 3;
159        } else if (primitive.isLong()) {
160            return 4;
161        } else if (primitive.isFloat()) {
162            return 5;
163        } else if (primitive.isDouble()) {
164            return 6;
165        } else {
166            return -1;
167        }
168    }
169
170    public static boolean isImplicitConversion(ModelClass from, ModelClass to) {
171        if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) {
172            if (from.isBoolean() || to.isBoolean() || to.isChar()) {
173                return false;
174            }
175            int fromConversionLevel = getImplicitConversionLevel(from);
176            int toConversionLevel = getImplicitConversionLevel(to);
177            return fromConversionLevel < toConversionLevel;
178        } else {
179            return false;
180        }
181    }
182}
183