| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.databinding.tool.reflection; |
| |
| import android.databinding.Bindable; |
| |
| import java.util.List; |
| |
| public abstract class ModelMethod { |
| public abstract ModelClass getDeclaringClass(); |
| |
| public abstract ModelClass[] getParameterTypes(); |
| |
| public abstract String getName(); |
| |
| public abstract ModelClass getReturnType(List<ModelClass> args); |
| |
| public abstract boolean isVoid(); |
| |
| public abstract boolean isPublic(); |
| |
| public abstract boolean isProtected(); |
| |
| public abstract boolean isStatic(); |
| |
| public abstract boolean isAbstract(); |
| |
| /** |
| * @return whether or not this method has been given the {@link Bindable} annotation. |
| */ |
| public abstract boolean isBindable(); |
| |
| /** |
| * Since when this method is available. Important for Binding expressions so that we don't |
| * call non-existing APIs when setting UI. |
| * |
| * @return The SDK_INT where this method was added. If it is not a framework method, should |
| * return 1. |
| */ |
| public abstract int getMinApi(); |
| |
| /** |
| * Returns the JNI description of the method which can be used to lookup it in SDK. |
| * @see TypeUtil |
| */ |
| public abstract String getJniDescription(); |
| |
| /** |
| * @return true if the final parameter is a varargs parameter. |
| */ |
| public abstract boolean isVarArgs(); |
| |
| /** |
| * @param args The arguments to the method |
| * @return Whether the arguments would be accepted as parameters to this method. |
| */ |
| public boolean acceptsArguments(List<ModelClass> args) { |
| boolean isVarArgs = isVarArgs(); |
| ModelClass[] parameterTypes = getParameterTypes(); |
| if ((!isVarArgs && args.size() != parameterTypes.length) || |
| (isVarArgs && args.size() < parameterTypes.length - 1)) { |
| return false; // The wrong number of parameters |
| } |
| boolean parametersMatch = true; |
| for (int i = 0; i < args.size(); i++) { |
| ModelClass parameterType = getParameter(i, parameterTypes); |
| ModelClass arg = args.get(i); |
| if (parameterType.isIncomplete()) { |
| parameterType = parameterType.erasure(); |
| } |
| if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) { |
| parametersMatch = false; |
| break; |
| } |
| } |
| return parametersMatch; |
| } |
| |
| public boolean isBetterArgMatchThan(ModelMethod other, List<ModelClass> args) { |
| final ModelClass[] parameterTypes = getParameterTypes(); |
| final ModelClass[] otherParameterTypes = other.getParameterTypes(); |
| for (int i = 0; i < args.size(); i++) { |
| final ModelClass arg = args.get(i); |
| final ModelClass thisParameter = getParameter(i, parameterTypes); |
| final ModelClass thatParameter = other.getParameter(i, otherParameterTypes); |
| if (thisParameter.equals(thatParameter)) { |
| continue; |
| } |
| final int diff = compareParameter(arg, thisParameter, thatParameter); |
| if (diff != 0) { |
| return diff < 0; |
| } |
| } |
| return false; |
| } |
| |
| public ModelClass getReturnType() { |
| return getReturnType(null); |
| } |
| |
| private ModelClass getParameter(int index, ModelClass[] parameterTypes) { |
| int normalParamCount = isVarArgs() ? parameterTypes.length - 1 : parameterTypes.length; |
| if (index < normalParamCount) { |
| return parameterTypes[index]; |
| } else { |
| return parameterTypes[parameterTypes.length - 1].getComponentType(); |
| } |
| } |
| |
| private static int compareParameter(ModelClass arg, ModelClass thisParameter, |
| ModelClass thatParameter) { |
| if (thatParameter.equals(arg)) { |
| return 1; |
| } else if (thisParameter.equals(arg)) { |
| return -1; |
| } else if (isBoxingConversion(thatParameter, arg)) { |
| return 1; |
| } else if (isBoxingConversion(thisParameter, arg)) { |
| // Boxing/unboxing is second best |
| return -1; |
| } else { |
| int argConversionLevel = getImplicitConversionLevel(arg); |
| if (argConversionLevel != -1) { |
| int oldConversionLevel = getImplicitConversionLevel(thatParameter); |
| int newConversionLevel = getImplicitConversionLevel(thisParameter); |
| if (newConversionLevel != -1 && |
| (oldConversionLevel == -1 || newConversionLevel < oldConversionLevel)) { |
| return -1; |
| } else if (oldConversionLevel != -1) { |
| return 1; |
| } |
| } |
| // Look for more exact match |
| if (thatParameter.isAssignableFrom(thisParameter)) { |
| return -1; |
| } |
| } |
| return 0; // no difference |
| } |
| |
| public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) { |
| if (class1.isPrimitive() != class2.isPrimitive()) { |
| return (class1.box().equals(class2.box())); |
| } else { |
| return false; |
| } |
| } |
| |
| public static int getImplicitConversionLevel(ModelClass primitive) { |
| if (primitive == null) { |
| return -1; |
| } else if (primitive.isByte()) { |
| return 0; |
| } else if (primitive.isChar()) { |
| return 1; |
| } else if (primitive.isShort()) { |
| return 2; |
| } else if (primitive.isInt()) { |
| return 3; |
| } else if (primitive.isLong()) { |
| return 4; |
| } else if (primitive.isFloat()) { |
| return 5; |
| } else if (primitive.isDouble()) { |
| return 6; |
| } else { |
| return -1; |
| } |
| } |
| |
| public static boolean isImplicitConversion(ModelClass from, ModelClass to) { |
| if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) { |
| if (from.isBoolean() || to.isBoolean() || to.isChar()) { |
| return false; |
| } |
| int fromConversionLevel = getImplicitConversionLevel(from); |
| int toConversionLevel = getImplicitConversionLevel(to); |
| return fromConversionLevel < toConversionLevel; |
| } else { |
| return false; |
| } |
| } |
| } |