private int isMethodInvocationConvertible()

in freemarker-core/src/main/java/freemarker/ext/beans/ArgumentTypes.java [532:626]


    private int isMethodInvocationConvertible(final Class<?> formal, final Class<?> actual) {
        // Check for identity or widening reference conversion
        if (formal.isAssignableFrom(actual) && actual != CharacterOrString.class) {
            return CONVERSION_DIFFICULTY_REFLECTION;
        } else if (bugfixed) {
            final Class<?> formalNP;
            if (formal.isPrimitive()) {
                if (actual == Null.class) {
                    return CONVERSION_DIFFICULTY_IMPOSSIBLE;
                }
                
                formalNP = ClassUtil.primitiveClassToBoxingClass(formal);
                if (actual == formalNP) {
                    // Character and char, etc.
                    return CONVERSION_DIFFICULTY_REFLECTION;
                }
            } else {  // formal is non-primitive
                if (actual == Null.class) {
                    return CONVERSION_DIFFICULTY_REFLECTION;
                }
                
                formalNP = formal;
            }
            if (Number.class.isAssignableFrom(actual) && Number.class.isAssignableFrom(formalNP)) {
                return OverloadedNumberUtil.getArgumentConversionPrice(actual, formalNP) == Integer.MAX_VALUE
                        ? CONVERSION_DIFFICULTY_IMPOSSIBLE : CONVERSION_DIFFICULTY_REFLECTION;
            } else if (formal.isArray()) {
                // BeansWrapper method/constructor calls convert from List to array automatically
                return List.class.isAssignableFrom(actual)
                        ? CONVERSION_DIFFICULTY_FREEMARKER : CONVERSION_DIFFICULTY_IMPOSSIBLE;
            } else if (actual.isArray() && formal.isAssignableFrom(List.class)) {
                // BeansWrapper method/constructor calls convert from array to List automatically
                return CONVERSION_DIFFICULTY_FREEMARKER;
            } else if (actual == CharacterOrString.class
                    && (formal.isAssignableFrom(String.class)
                            || formal.isAssignableFrom(Character.class) || formal == char.class)) {
                return CONVERSION_DIFFICULTY_FREEMARKER;
            } else {
                return CONVERSION_DIFFICULTY_IMPOSSIBLE;
            }
        } else { // if !bugfixed
            // This non-bugfixed (backward-compatible, pre-2.3.21) branch:
            // - Doesn't convert *to* non-primitive numerical types (unless the argument is a BigDecimal).
            //   (This is like in Java language, which also doesn't coerce to non-primitive numerical types.) 
            // - Doesn't support BigInteger conversions
            // - Doesn't support NumberWithFallbackType-s and CharacterOrString-s. Those are only produced in bugfixed
            //   mode anyway.
            // - Doesn't support conversion between array and List
            if (formal.isPrimitive()) {
                // Check for boxing with widening primitive conversion. Note that 
                // actual parameters are never primitives.
                // It doesn't do the same with boxing types... that was a bug.
                if (formal == Boolean.TYPE) {
                    return actual == Boolean.class
                            ? CONVERSION_DIFFICULTY_REFLECTION : CONVERSION_DIFFICULTY_IMPOSSIBLE;
                } else if (formal == Double.TYPE && 
                        (actual == Double.class || actual == Float.class || 
                         actual == Long.class || actual == Integer.class || 
                         actual == Short.class || actual == Byte.class)) {
                     return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (formal == Integer.TYPE && 
                        (actual == Integer.class || actual == Short.class || 
                         actual == Byte.class)) {
                     return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (formal == Long.TYPE && 
                        (actual == Long.class || actual == Integer.class || 
                         actual == Short.class || actual == Byte.class)) {
                     return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (formal == Float.TYPE && 
                        (actual == Float.class || actual == Long.class || 
                         actual == Integer.class || actual == Short.class || 
                         actual == Byte.class)) {
                     return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (formal == Character.TYPE) {
                    return actual == Character.class
                            ? CONVERSION_DIFFICULTY_REFLECTION : CONVERSION_DIFFICULTY_IMPOSSIBLE;
                } else if (formal == Byte.TYPE && actual == Byte.class) {
                    return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (formal == Short.TYPE &&
                   (actual == Short.class || actual == Byte.class)) {
                    return CONVERSION_DIFFICULTY_REFLECTION;
                } else if (BigDecimal.class.isAssignableFrom(actual) && ClassUtil.isNumerical(formal)) {
                    // Special case for BigDecimals as we deem BigDecimal to be
                    // convertible to any numeric type - either object or primitive.
                    // This can actually cause us trouble as this is a narrowing 
                    // conversion, not widening. 
                    return CONVERSION_DIFFICULTY_REFLECTION;
                } else {
                    return CONVERSION_DIFFICULTY_IMPOSSIBLE;
                }
            } else {
                return CONVERSION_DIFFICULTY_IMPOSSIBLE;
            }
        }
    }