• 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 
isStatic()35     public abstract boolean isStatic();
36 
isAbstract()37     public abstract boolean isAbstract();
38 
39     /**
40      * @return whether or not this method has been given the {@link Bindable} annotation.
41      */
isBindable()42     public abstract boolean isBindable();
43 
44     /**
45      * Since when this method is available. Important for Binding expressions so that we don't
46      * call non-existing APIs when setting UI.
47      *
48      * @return The SDK_INT where this method was added. If it is not a framework method, should
49      * return 1.
50      */
getMinApi()51     public abstract int getMinApi();
52 
53     /**
54      * Returns the JNI description of the method which can be used to lookup it in SDK.
55      * @see TypeUtil
56      */
getJniDescription()57     public abstract String getJniDescription();
58 
59     /**
60      * @return true if the final parameter is a varargs parameter.
61      */
isVarArgs()62     public abstract boolean isVarArgs();
63 
64     /**
65      * @param args The arguments to the method
66      * @return Whether the arguments would be accepted as parameters to this method.
67      */
acceptsArguments(List<ModelClass> args)68     public boolean acceptsArguments(List<ModelClass> args) {
69         boolean isVarArgs = isVarArgs();
70         ModelClass[] parameterTypes = getParameterTypes();
71         if ((!isVarArgs && args.size() != parameterTypes.length) ||
72                 (isVarArgs && args.size() < parameterTypes.length - 1)) {
73             return false; // The wrong number of parameters
74         }
75         boolean parametersMatch = true;
76         for (int i = 0; i < args.size(); i++) {
77             ModelClass parameterType = getParameter(i, parameterTypes);
78             ModelClass arg = args.get(i);
79             if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) {
80                 parametersMatch = false;
81                 break;
82             }
83         }
84         return parametersMatch;
85     }
86 
isBetterArgMatchThan(ModelMethod other, List<ModelClass> args)87     public boolean isBetterArgMatchThan(ModelMethod other, List<ModelClass> args) {
88         final ModelClass[] parameterTypes = getParameterTypes();
89         final ModelClass[] otherParameterTypes = other.getParameterTypes();
90         for (int i = 0; i < args.size(); i++) {
91             final ModelClass arg = args.get(i);
92             final ModelClass thisParameter = getParameter(i, parameterTypes);
93             final ModelClass thatParameter = other.getParameter(i, otherParameterTypes);
94             final int diff = compareParameter(arg, thisParameter, thatParameter);
95             if (diff != 0) {
96                 return diff < 0;
97             }
98         }
99         return false;
100     }
101 
getParameter(int index, ModelClass[] parameterTypes)102     private ModelClass getParameter(int index, ModelClass[] parameterTypes) {
103         int normalParamCount = isVarArgs() ? parameterTypes.length - 1 : parameterTypes.length;
104         if (index < normalParamCount) {
105             return parameterTypes[index];
106         } else {
107             return parameterTypes[parameterTypes.length - 1].getComponentType();
108         }
109     }
110 
compareParameter(ModelClass arg, ModelClass thisParameter, ModelClass thatParameter)111     private static int compareParameter(ModelClass arg, ModelClass thisParameter,
112             ModelClass thatParameter) {
113         if (thatParameter.equals(arg)) {
114             return 1;
115         } else if (thisParameter.equals(arg)) {
116             return -1;
117         } else if (isBoxingConversion(thatParameter, arg)) {
118             return 1;
119         } else if (isBoxingConversion(thisParameter, arg)) {
120             // Boxing/unboxing is second best
121             return -1;
122         } else {
123             int argConversionLevel = getImplicitConversionLevel(arg);
124             if (argConversionLevel != -1) {
125                 int oldConversionLevel = getImplicitConversionLevel(thatParameter);
126                 int newConversionLevel = getImplicitConversionLevel(thisParameter);
127                 if (newConversionLevel != -1 &&
128                         (oldConversionLevel == -1 || newConversionLevel < oldConversionLevel)) {
129                     return -1;
130                 } else if (oldConversionLevel != -1) {
131                     return 1;
132                 }
133             }
134             // Look for more exact match
135             if (thatParameter.isAssignableFrom(thisParameter)) {
136                 return -1;
137             }
138         }
139         return 0; // no difference
140     }
141 
isBoxingConversion(ModelClass class1, ModelClass class2)142     public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) {
143         if (class1.isPrimitive() != class2.isPrimitive()) {
144             return (class1.box().equals(class2.box()));
145         } else {
146             return false;
147         }
148     }
149 
getImplicitConversionLevel(ModelClass primitive)150     public static int getImplicitConversionLevel(ModelClass primitive) {
151         if (primitive == null) {
152             return -1;
153         } else if (primitive.isByte()) {
154             return 0;
155         } else if (primitive.isChar()) {
156             return 1;
157         } else if (primitive.isShort()) {
158             return 2;
159         } else if (primitive.isInt()) {
160             return 3;
161         } else if (primitive.isLong()) {
162             return 4;
163         } else if (primitive.isFloat()) {
164             return 5;
165         } else if (primitive.isDouble()) {
166             return 6;
167         } else {
168             return -1;
169         }
170     }
171 
isImplicitConversion(ModelClass from, ModelClass to)172     public static boolean isImplicitConversion(ModelClass from, ModelClass to) {
173         if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) {
174             if (from.isBoolean() || to.isBoolean() || to.isChar()) {
175                 return false;
176             }
177             int fromConversionLevel = getImplicitConversionLevel(from);
178             int toConversionLevel = getImplicitConversionLevel(to);
179             return fromConversionLevel < toConversionLevel;
180         } else {
181             return false;
182         }
183     }
184 }
185