Search in sources :

Example 1 with WrapperTemplateModel

use of freemarker.ext.util.WrapperTemplateModel in project freemarker by apache.

the class DefaultObjectWrapperTest method assertRoundtrip.

private void assertRoundtrip(DefaultObjectWrapper dow, Object obj, Class expectedTMClass, Class expectedPojoClass, String expectedPojoToString) throws TemplateModelException {
    final TemplateModel objTM = dow.wrap(obj);
    assertTrue(expectedTMClass.isAssignableFrom(objTM.getClass()));
    final TemplateHashModel testBeanTM = (TemplateHashModel) dow.wrap(new RoundtripTesterBean());
    {
        TemplateMethodModelEx getClassM = (TemplateMethodModelEx) testBeanTM.get("getClass");
        Object r = getClassM.exec(Collections.singletonList(objTM));
        final Class rClass = (Class) ((WrapperTemplateModel) r).getWrappedObject();
        assertTrue(rClass + " instanceof " + expectedPojoClass, expectedPojoClass.isAssignableFrom(rClass));
    }
    if (expectedPojoToString != null) {
        TemplateMethodModelEx getToStringM = (TemplateMethodModelEx) testBeanTM.get("toString");
        Object r = getToStringM.exec(Collections.singletonList(objTM));
        assertEquals(expectedPojoToString, ((TemplateScalarModel) r).getAsString());
    }
}
Also used : WrapperTemplateModel(freemarker.ext.util.WrapperTemplateModel) WrapperTemplateModel(freemarker.ext.util.WrapperTemplateModel)

Example 2 with WrapperTemplateModel

use of freemarker.ext.util.WrapperTemplateModel in project freemarker by apache.

the class BeansWrapper method tryUnwrapTo.

/**
 * See {@link #tryUnwrapTo(TemplateModel, Class, int)}.
 */
private Object tryUnwrapTo(final TemplateModel model, Class<?> targetClass, final int typeFlags, final Map<Object, Object> recursionStops) throws TemplateModelException {
    if (model == null || model == nullModel) {
        return null;
    }
    final boolean is2321Bugfixed = is2321Bugfixed();
    if (is2321Bugfixed && targetClass.isPrimitive()) {
        targetClass = ClassUtil.primitiveClassToBoxingClass(targetClass);
    }
    // passed as an argument to TemplateMethodModelEx etc.
    if (model instanceof AdapterTemplateModel) {
        Object wrapped = ((AdapterTemplateModel) model).getAdaptedObject(targetClass);
        if (targetClass == Object.class || targetClass.isInstance(wrapped)) {
            return wrapped;
        }
        // Attempt numeric conversion:
        if (targetClass != Object.class && (wrapped instanceof Number && ClassUtil.isNumerical(targetClass))) {
            Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass, is2321Bugfixed);
            if (number != null)
                return number;
        }
    }
    if (model instanceof WrapperTemplateModel) {
        Object wrapped = ((WrapperTemplateModel) model).getWrappedObject();
        if (targetClass == Object.class || targetClass.isInstance(wrapped)) {
            return wrapped;
        }
        // Attempt numeric conversion:
        if (targetClass != Object.class && (wrapped instanceof Number && ClassUtil.isNumerical(targetClass))) {
            Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass, is2321Bugfixed);
            if (number != null) {
                return number;
            }
        }
    }
    // know what is expected as the return type.
    if (targetClass != Object.class) {
        // [2.4][IcI]: Should also check for CharSequence at the end
        if (String.class == targetClass) {
            if (model instanceof TemplateScalarModel) {
                return ((TemplateScalarModel) model).getAsString();
            }
            // String is final, so no other conversion will work
            return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
        }
        // Primitive numeric types & Number.class and its subclasses
        if (ClassUtil.isNumerical(targetClass)) {
            if (model instanceof TemplateNumberModel) {
                Number number = forceUnwrappedNumberToType(((TemplateNumberModel) model).getAsNumber(), targetClass, is2321Bugfixed);
                if (number != null) {
                    return number;
                }
            }
        }
        if (boolean.class == targetClass || Boolean.class == targetClass) {
            if (model instanceof TemplateBooleanModel) {
                return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
            }
            // Boolean is final, no other conversion will work
            return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
        }
        if (Map.class == targetClass) {
            if (model instanceof TemplateHashModel) {
                return new HashAdapter((TemplateHashModel) model, this);
            }
        }
        if (List.class == targetClass) {
            if (model instanceof TemplateSequenceModel) {
                return new SequenceAdapter((TemplateSequenceModel) model, this);
            }
        }
        if (Set.class == targetClass) {
            if (model instanceof TemplateCollectionModel) {
                return new SetAdapter((TemplateCollectionModel) model, this);
            }
        }
        if (Collection.class == targetClass || Iterable.class == targetClass) {
            if (model instanceof TemplateCollectionModel) {
                return new CollectionAdapter((TemplateCollectionModel) model, this);
            }
            if (model instanceof TemplateSequenceModel) {
                return new SequenceAdapter((TemplateSequenceModel) model, this);
            }
        }
        // TemplateSequenceModels can be converted to arrays
        if (targetClass.isArray()) {
            if (model instanceof TemplateSequenceModel) {
                return unwrapSequenceToArray((TemplateSequenceModel) model, targetClass, true, recursionStops);
            }
            // array classes are final, no other conversion will work
            return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
        }
        // Allow one-char strings to be coerced to characters
        if (char.class == targetClass || targetClass == Character.class) {
            if (model instanceof TemplateScalarModel) {
                String s = ((TemplateScalarModel) model).getAsString();
                if (s.length() == 1) {
                    return Character.valueOf(s.charAt(0));
                }
            }
            // Character is final, no other conversion will work
            return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
        }
        if (Date.class.isAssignableFrom(targetClass) && model instanceof TemplateDateModel) {
            Date date = ((TemplateDateModel) model).getAsDate();
            if (targetClass.isInstance(date)) {
                return date;
            }
        }
    }
    // End: if (targetClass != Object.class)
    // Since the targetClass was of no help initially, now we use
    // a quite arbitrary order in which we walk through the TemplateModel subinterfaces, and unwrapp them to
    // their "natural" Java correspondent. We still try exclude unwrappings that won't fit the target parameter
    // type(s). This is mostly important because of multi-typed FTL values that could be unwrapped on multiple ways.
    // Iteration's Type Flags. Should be always 0 for non-overloaded and when !is2321Bugfixed.
    int itf = typeFlags;
    // returned, once more with itf == 0. Otherwise we execute this once with itf == 0.
    do {
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_NUMBER) != 0) && model instanceof TemplateNumberModel) {
            Number number = ((TemplateNumberModel) model).getAsNumber();
            if (itf != 0 || targetClass.isInstance(number)) {
                return number;
            }
        }
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_DATE) != 0) && model instanceof TemplateDateModel) {
            Date date = ((TemplateDateModel) model).getAsDate();
            if (itf != 0 || targetClass.isInstance(date)) {
                return date;
            }
        }
        if ((itf == 0 || (itf & (TypeFlags.ACCEPTS_STRING | TypeFlags.CHARACTER)) != 0) && model instanceof TemplateScalarModel && (itf != 0 || targetClass.isAssignableFrom(String.class))) {
            String strVal = ((TemplateScalarModel) model).getAsString();
            if (itf == 0 || (itf & TypeFlags.CHARACTER) == 0) {
                return strVal;
            } else {
                // TypeFlags.CHAR == 1
                if (strVal.length() == 1) {
                    if ((itf & TypeFlags.ACCEPTS_STRING) != 0) {
                        return new CharacterOrString(strVal);
                    } else {
                        return Character.valueOf(strVal.charAt(0));
                    }
                } else if ((itf & TypeFlags.ACCEPTS_STRING) != 0) {
                    return strVal;
                }
            // It had to be unwrapped to Character, but the string length wasn't 1 => Fall through
            }
        }
        // Should be earlier than TemplateScalarModel, but we keep it here until FM 2.4 or such
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_BOOLEAN) != 0) && model instanceof TemplateBooleanModel && (itf != 0 || targetClass.isAssignableFrom(Boolean.class))) {
            return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
        }
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_MAP) != 0) && model instanceof TemplateHashModel && (itf != 0 || targetClass.isAssignableFrom(HashAdapter.class))) {
            return new HashAdapter((TemplateHashModel) model, this);
        }
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_LIST) != 0) && model instanceof TemplateSequenceModel && (itf != 0 || targetClass.isAssignableFrom(SequenceAdapter.class))) {
            return new SequenceAdapter((TemplateSequenceModel) model, this);
        }
        if ((itf == 0 || (itf & TypeFlags.ACCEPTS_SET) != 0) && model instanceof TemplateCollectionModel && (itf != 0 || targetClass.isAssignableFrom(SetAdapter.class))) {
            return new SetAdapter((TemplateCollectionModel) model, this);
        }
        // enough to check if the TypeFlags.ACCEPTS_ARRAY bit is 1:
        if ((itf & TypeFlags.ACCEPTS_ARRAY) != 0 && model instanceof TemplateSequenceModel) {
            return new SequenceAdapter((TemplateSequenceModel) model, this);
        }
        if (itf == 0) {
            break;
        }
        // start 2nd iteration
        itf = 0;
    } while (true);
    // Note that this will be always true for Object.class targetClass.
    if (targetClass.isInstance(model)) {
        return model;
    }
    return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
}
Also used : TemplateSequenceModel(freemarker.template.TemplateSequenceModel) TemplateNumberModel(freemarker.template.TemplateNumberModel) TemplateDateModel(freemarker.template.TemplateDateModel) AdapterTemplateModel(freemarker.template.AdapterTemplateModel) Date(java.util.Date) TemplateHashModel(freemarker.template.TemplateHashModel) Collection(java.util.Collection) AccessibleObject(java.lang.reflect.AccessibleObject) TemplateScalarModel(freemarker.template.TemplateScalarModel) WrapperTemplateModel(freemarker.ext.util.WrapperTemplateModel) TemplateCollectionModel(freemarker.template.TemplateCollectionModel) TemplateBooleanModel(freemarker.template.TemplateBooleanModel)

Aggregations

WrapperTemplateModel (freemarker.ext.util.WrapperTemplateModel)2 AdapterTemplateModel (freemarker.template.AdapterTemplateModel)1 TemplateBooleanModel (freemarker.template.TemplateBooleanModel)1 TemplateCollectionModel (freemarker.template.TemplateCollectionModel)1 TemplateDateModel (freemarker.template.TemplateDateModel)1 TemplateHashModel (freemarker.template.TemplateHashModel)1 TemplateNumberModel (freemarker.template.TemplateNumberModel)1 TemplateScalarModel (freemarker.template.TemplateScalarModel)1 TemplateSequenceModel (freemarker.template.TemplateSequenceModel)1 AccessibleObject (java.lang.reflect.AccessibleObject)1 Collection (java.util.Collection)1 Date (java.util.Date)1