/* * 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 args); public abstract boolean isVoid(); public abstract boolean isPublic(); 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 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.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) { parametersMatch = false; break; } } return parametersMatch; } public boolean isBetterArgMatchThan(ModelMethod other, List 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); final int diff = compareParameter(arg, thisParameter, thatParameter); if (diff != 0) { return diff < 0; } } return false; } 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; } } }