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