Home | History | Annotate | Download | only in reflection
      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