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