if (!propertyType.isAssignableFrom(adapter.getType()))
throw new RuntimeException(ServiceMessages.propertyTypeMismatch(propertyName, sourceClass,
adapter.getType(), propertyType));
ClassInstantiator instantiator = proxyFactory.createProxy(propertyType, new PlasticClassTransformer()
{
public void transform(PlasticClass plasticClass)
{
final PlasticField sourceField = plasticClass.introduceField(sourceClass, "source").inject(source);
PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(propertyType.getName(),
"readProperty", null, null);
// You don't do this using MethodAdvice, because then we'd have to use reflection to access the read
// method.
delegateMethod.changeImplementation(new InstructionBuilderCallback()
{
public void doBuild(InstructionBuilder builder)
{
builder.loadThis().getField(sourceField);
builder.invoke(sourceClass, propertyType, adapter.getReadMethod().getName());
// Now add the null check.
builder.dupe().when(Condition.NULL, new InstructionBuilderCallback()
{
public void doBuild(InstructionBuilder builder)
{
builder.throwException(
NullPointerException.class,
String.format(
"Unable to delegate method invocation to property '%s' of %s, because the property is null.",
propertyName, source));
}
});
builder.returnResult();
}
});
for (Method m : propertyType.getMethods())
{
plasticClass.introduceMethod(m).delegateTo(delegateMethod);
}
plasticClass.addToString(String.format("<Shadow: property %s of %s>", propertyName, source));
}
});
return propertyType.cast(instantiator.newInstance());
}