Monday, March 19, 2007

Backwards Magic

Just spent a long, long time on this puppy. I couldn't get the overridden methods on a Form object to see a variable reference at the Page level from inside a closure.

class CompClosuresPage extends WebPage {

SomeData someData = new SomeData(name:"heyo", value:"someval")

public CompClosuresPage()
{
Closure testClosure = {
println someData.name
}

Class newFormClass = CompClosuresPageTestForm.generateClassInJava(testClosure)
Form form = newFormClass.getConstructor((Class [])[String.class]).newInstance((Object[])['baseForm'] )
form.setModel(new CompoundPropertyModel(someData))
form.onTestEvent()
}
}

Pretend 'onTestEvent' is like 'onSubmit'.

This fails. 'CompClosuresPageTestForm' is a test class I came up with that extends a Wicket Component of some type. If I remove that base class, the code above works.

Anyway, very long story short, If I changed the base class to a non-Wicket one, it worked. A Wicket one, it didn't. After a long walk all over the class hierarchy, I sorted it out. Inside wicket.Component:

/**
* Gets the component at the given path.
*
* @param path
* Path to component
* @return The component at the path
*/
Component get(final String path)
{
// Path to this component is an empty path
if (path.equals(""))
{
return this;
}
throw new IllegalArgumentException(
exceptionMessage("Component is not a container and so does not contain the path "
+ path));
}

It looks like Groovy is calling this all the time. I'm not sure what to do about it. At 'wicket.MarkupContainer', its final, so I can't override it and try to handle it gracefully. I can't really try to change the guts of groovy either. For today, if you want to access a class level property from an overridden closure, you'll have to call the getter method (implicitly or explicitly defined). The above closure would have to be written as follows:

Closure testClosure = {
println getSomeData().name
}

Ok, I spent a little more time on this. It is in fact the issue. The MetaClass picks up the 'get(String)' method as the general property accessor method. What I tried to do was put a delegating metaclass into the registry for that class type after its dynamically generated. Then, if the access fails, bubble that up and it should check the Page class. Its not working for some reason, though, and I've spent too much time on it today. I'll leave the MetaClass class in the code and get back to it later. I think without altering either groovy or wicket, that would be the way to go.

No comments: