/**
* Generates method body for recursively unwrapping a {@link BindableProxy}.
*/
private Statement generateDeepUnwrapMethodBody(final String methodName) {
final String cloneVar = "clone";
final BlockStatement block = new BlockStatement();
block.addStatement(Stmt.declareFinalVariable(cloneVar, bindable, Stmt.newObject(bindable)));
for (final String property : bindable.getBeanDescriptor().getProperties()) {
final MetaMethod readMethod = bindable.getBeanDescriptor().getReadMethodForProperty(property);
final MetaMethod writeMethod = bindable.getBeanDescriptor().getWriteMethodForProperty(property);
if (readMethod != null && writeMethod != null) {
MetaClass type = readMethod.getReturnType();
if (!DataBindingUtil.isBindableType(type)) {
// If we find a collection we copy its elements and unwrap them if necessary
// TODO support map types
if (type.isAssignableTo(Collection.class)) {
String colVarName = property + "Clone";
String elemVarName = property + "Elem";
BlockBuilder<ElseBlockBuilder> colBlock = If.isNotNull(Stmt.nestedCall(target().invoke(readMethod)));
if ((type.isInterface() || type.isAbstract()) &&
(type.isAssignableTo(List.class) || type.isAssignableTo(Set.class))) {
MetaClass clazz = (type.isAssignableTo(Set.class))
? MetaClassFactory.get(HashSet.class) : MetaClassFactory.get(ArrayList.class);
colBlock.append(Stmt.declareFinalVariable(colVarName, type.getErased(), Stmt.newObject(clazz)));
}
else {
if (!type.isInterface() && !type.isAbstract()) {
colBlock.append(Stmt.declareFinalVariable(colVarName, type.getErased(), Stmt.newObject(type.getErased())));
}
else {
logger.log(TreeLogger.WARN, "Bean validation on collection " + property + " in class " + bindable +
" won't work. Change to either List or Set or use a concrete type instead.");
continue;
}
}
// Check if the collection element is proxied and unwrap if necessary
colBlock.append(
Stmt.nestedCall(target().invoke(readMethod)).foreach(elemVarName, Object.class)
.append (
If.instanceOf(Refs.get(elemVarName), BindableProxy.class)
.append (Stmt.loadVariable(colVarName)
.invoke("add", Stmt.castTo(BindableProxy.class, Stmt.loadVariable(elemVarName)).invoke(methodName))
)
.finish()
.else_()
.append(Stmt.loadVariable(colVarName).invoke("add", Refs.get(elemVarName)))
.finish()
)
.finish());
colBlock.append(Stmt.loadVariable(cloneVar).invoke(writeMethod, Refs.get(colVarName)));
block.addStatement(colBlock.finish());
}
else {
block.addStatement(Stmt.loadVariable(cloneVar).invoke(writeMethod,target().invoke(readMethod)));
}
}
// Found a bindable property: Generate code to unwrap for the case the instance is proxied
else {
final Statement field = target().invoke(readMethod);
block.addStatement (
If.instanceOf(field, BindableProxy.class)
.append(Stmt.loadVariable(cloneVar).invoke(writeMethod,
Cast.to (
readMethod.getReturnType(),
Stmt.castTo(BindableProxy.class, Stmt.loadVariable("this").invoke(readMethod))
.invoke(methodName)
)
)
)
.finish()
.else_()
.append(Stmt.loadVariable(cloneVar).invoke(writeMethod, target().invoke(readMethod)))
.finish()
);
}
}
}
block.addStatement(Stmt.loadVariable(cloneVar).returnValue());
return block;
}