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 { 23 public abstract ModelClass getDeclaringClass(); 24 25 public abstract ModelClass[] getParameterTypes(); 26 27 public abstract String getName(); 28 29 public abstract ModelClass getReturnType(List<ModelClass> args); 30 31 public abstract boolean isVoid(); 32 33 public abstract boolean isPublic(); 34 35 public abstract boolean isProtected(); 36 37 public abstract boolean isStatic(); 38 39 public abstract boolean isAbstract(); 40 41 /** 42 * @return whether or not this method has been given the {@link Bindable} annotation. 43 */ 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 */ 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 */ 59 public abstract String getJniDescription(); 60 61 /** 62 * @return true if the final parameter is a varargs parameter. 63 */ 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 */ 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 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 110 public ModelClass getReturnType() { 111 return getReturnType(null); 112 } 113 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 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 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 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 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