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());
}
}
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;
}
Aggregations