int compareParameterListPreferability()

in freemarker-core/src/main/java/freemarker/ext/beans/ArgumentTypes.java [176:402]


    int compareParameterListPreferability(Class<?>[] paramTypes1, Class<?>[] paramTypes2, boolean varArg) {
        final int argTypesLen = types.length; 
        final int paramTypes1Len = paramTypes1.length;
        final int paramTypes2Len = paramTypes2.length;
        //assert varArg || paramTypes1Len == paramTypes2Len;
        
        if (bugfixed) {
            int paramList1WeakWinCnt = 0;
            int paramList2WeakWinCnt = 0;
            int paramList1WinCnt = 0;
            int paramList2WinCnt = 0;
            int paramList1StrongWinCnt = 0;
            int paramList2StrongWinCnt = 0;
            int paramList1VeryStrongWinCnt = 0;
            int paramList2VeryStrongWinCnt = 0;
            int firstWinerParamList = 0;
            for (int i = 0; i < argTypesLen; i++) {
                final Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg);
                final Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg);
                
                final int winerParam;  // 1 => paramType1; -1 => paramType2; 0 => draw
                if (paramType1 == paramType2) {
                    winerParam = 0;
                } else {
                    final Class<?> argType = types[i];
                    final boolean argIsNum = Number.class.isAssignableFrom(argType);
                    
                    final int numConvPrice1;
                    if (argIsNum && ClassUtil.isNumerical(paramType1)) {
                        final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
                                ? ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1; 
                        numConvPrice1 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType1);
                    } else {
                        numConvPrice1 = Integer.MAX_VALUE;
                    }
                    // numConvPrice1 is Integer.MAX_VALUE if either:
                    // - argType and paramType1 aren't both numerical
                    // - FM doesn't know some of the numerical types, or the conversion between them is not allowed    
                    
                    final int numConvPrice2;
                    if (argIsNum && ClassUtil.isNumerical(paramType2)) {
                        final Class<?> nonPrimParamType2 = paramType2.isPrimitive()
                                ? ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2; 
                        numConvPrice2 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType2);
                    } else {
                        numConvPrice2 = Integer.MAX_VALUE;
                    }
                    
                    if (numConvPrice1 == Integer.MAX_VALUE) {
                        if (numConvPrice2 == Integer.MAX_VALUE) {  // No numerical conversions anywhere
                            // List to array conversions (unwrapping sometimes makes a List instead of an array)
                            if (List.class.isAssignableFrom(argType)
                                    && (paramType1.isArray() || paramType2.isArray())) {
                                if (paramType1.isArray()) {
                                    if (paramType2.isArray()) {  // both paramType1 and paramType2 are arrays
                                        int r = compareParameterListPreferability_cmpTypeSpecificty(
                                                paramType1.getComponentType(), paramType2.getComponentType());
                                        // Because we don't know if the List items are instances of the component
                                        // type or not, we prefer the safer choice, which is the more generic array:
                                        if (r > 0) {
                                            winerParam = 2;
                                            paramList2StrongWinCnt++;
                                        } else if (r < 0) {
                                            winerParam = 1;
                                            paramList1StrongWinCnt++;
                                        } else {
                                            winerParam = 0;
                                        }
                                    } else {  // paramType1 is array, paramType2 isn't
                                        // Avoid List to array conversion if the other way makes any sense:
                                        if (Collection.class.isAssignableFrom(paramType2)) {
                                            winerParam = 2;
                                            paramList2StrongWinCnt++;
                                        } else {
                                            winerParam = 1;
                                            paramList1WeakWinCnt++;
                                        }
                                    }
                                } else {  // paramType2 is array, paramType1 isn't
                                    // Avoid List to array conversion if the other way makes any sense:
                                    if (Collection.class.isAssignableFrom(paramType1)) {
                                        winerParam = 1;
                                        paramList1StrongWinCnt++;
                                    } else {
                                        winerParam = 2;
                                        paramList2WeakWinCnt++;
                                    }
                                }
                            } else if (argType.isArray()
                                    && (List.class.isAssignableFrom(paramType1)
                                            || List.class.isAssignableFrom(paramType2))) {
                                // Array to List conversions (unwrapping sometimes makes an array instead of a List)
                                if (List.class.isAssignableFrom(paramType1)) {
                                    if (List.class.isAssignableFrom(paramType2)) {
                                        // Both paramType1 and paramType2 extends List
                                        winerParam = 0;
                                    } else {
                                        // Only paramType1 extends List
                                        winerParam = 2;
                                        paramList2VeryStrongWinCnt++;
                                    }
                                } else {
                                    // Only paramType2 extends List
                                    winerParam = 1;
                                    paramList1VeryStrongWinCnt++;
                                }
                            } else {  // No list to/from array conversion
                                final int r = compareParameterListPreferability_cmpTypeSpecificty(
                                        paramType1, paramType2);
                                if (r > 0) {
                                    winerParam = 1;
                                    if (r > 1) {
                                        paramList1WinCnt++;
                                    } else {
                                        paramList1WeakWinCnt++;
                                    }
                                } else if (r < 0) {
                                    winerParam = -1;
                                    if (r < -1) {
                                        paramList2WinCnt++;
                                    } else {
                                        paramList2WeakWinCnt++;
                                    }
                                } else {
                                    winerParam = 0;
                                }
                            }
                        } else {  // No num. conv. of param1, num. conv. of param2
                            winerParam = -1;
                            paramList2WinCnt++;
                        }
                    } else if (numConvPrice2 == Integer.MAX_VALUE) {  // Num. conv. of param1, not of param2
                        winerParam = 1;
                        paramList1WinCnt++;
                    } else {  // Num. conv. of both param1 and param2
                        if (numConvPrice1 != numConvPrice2) {
                            if (numConvPrice1 < numConvPrice2) {
                                winerParam = 1;
                                if (numConvPrice1 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
                                        && numConvPrice2 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
                                    paramList1StrongWinCnt++;
                                } else {
                                    paramList1WinCnt++;
                                }
                            } else {
                                winerParam = -1;
                                if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
                                        && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
                                    paramList2StrongWinCnt++;
                                } else {
                                    paramList2WinCnt++;
                                }
                            }
                        } else {
                            winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0);
                            if (winerParam == 1) paramList1WeakWinCnt++;
                            else if (winerParam == -1) paramList2WeakWinCnt++;
                        }
                    }
                }  // when paramType1 != paramType2
                
                if (firstWinerParamList == 0 && winerParam != 0) {
                    firstWinerParamList = winerParam; 
                }
            }  // for each parameter types
            
            if (paramList1VeryStrongWinCnt != paramList2VeryStrongWinCnt) {
                return paramList1VeryStrongWinCnt - paramList2VeryStrongWinCnt;
            } else if (paramList1StrongWinCnt != paramList2StrongWinCnt) {
                return paramList1StrongWinCnt - paramList2StrongWinCnt;
            } else if (paramList1WinCnt != paramList2WinCnt) {
                return paramList1WinCnt - paramList2WinCnt;
            } else if (paramList1WeakWinCnt != paramList2WeakWinCnt) {
                return paramList1WeakWinCnt - paramList2WeakWinCnt;
            } else if (firstWinerParamList != 0) {  // paramList1WinCnt == paramList2WinCnt
                return firstWinerParamList;
            } else { // still undecided
                if (varArg) {
                    if (paramTypes1Len == paramTypes2Len) {
                        // If we had a 0-length varargs array in both methods, we also compare the types at the
                        // index of the varargs parameter, like if we had a single varargs argument. However, this
                        // time we don't have an argument type, so we can only decide based on type specificity:
                        if (argTypesLen == paramTypes1Len - 1) {
                            Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, argTypesLen, true);
                            Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, argTypesLen, true);
                            if (ClassUtil.isNumerical(paramType1) && ClassUtil.isNumerical(paramType2)) {
                                int r = OverloadedNumberUtil.compareNumberTypeSpecificity(paramType1, paramType2);
                                if (r != 0) return r;
                                // falls through
                            }
                            return compareParameterListPreferability_cmpTypeSpecificty(paramType1, paramType2);
                        } else {
                            return 0;
                        }
                    } else {
                        // The method with more fixed parameters wins:
                        return paramTypes1Len - paramTypes2Len;
                    }
                } else {
                    return 0;
                }
            }
        } else { // non-bugfixed (backward-compatible) mode
            boolean paramTypes1HasAMoreSpecific = false;
            boolean paramTypes2HasAMoreSpecific = false;
            for (int i = 0; i < paramTypes1Len; ++i) {
                Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg);
                Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg);
                if (paramType1 != paramType2) {
                    paramTypes1HasAMoreSpecific = 
                        paramTypes1HasAMoreSpecific
                        || _MethodUtil.isMoreOrSameSpecificParameterType(paramType1, paramType2, false, 0) != 0;
                    paramTypes2HasAMoreSpecific = 
                        paramTypes2HasAMoreSpecific
                        || _MethodUtil.isMoreOrSameSpecificParameterType(paramType2, paramType1, false, 0) != 0;
                }
            }
            
            if (paramTypes1HasAMoreSpecific) {
                return paramTypes2HasAMoreSpecific ? 0 : 1;
            } else if (paramTypes2HasAMoreSpecific) {
                return -1;
            } else {
                return 0;
            }
        }
    }