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