jstrachan | 29 Jan 08:07 2005

Re: Builder ideas

John Rose wrote:

> On Jan 26, 2005, at 21:44, jstrachan wrote:
>> What we could do is have a Markup/Builder interface that the markup 
>> invokes; then classes can implement the specific Markup interface if 
>> they want - or we could provide an implementation which delegates 
>> Markup method calls to methods on the builder object. Then we don't 
>> have to mess with the core method invocation mecahnism
> I think this is backwards.
> Don't we implement an XML builder by creating a sort of Expando object? 
>  It intercepts all its method calls and funnels them through a common 
> "onElement" sort of call.
> def xml1 = ...
> def xmlb = makeExpandoMethodInterceptorThingy { name, attrs, content | 
> xml1.buildXMLElement(name, attrs, content) }
> with (xmlb) { head { body { } } }
> // == EQUIVALENT TO ==
> xmlb.head { xmlb.body { } } }
> // == EQUIVALENT TO ==
> xml1.buildXMLElement("head", []) { xml1.buildXMLElement("body", []) { } } }
> // == EQUIVALENT TO ==
> with (xml1) { buildXMLElement ("head", []) { buildXMLElement("body", []) 
> { } } }

Yes. I guess I was wondering, should developers write these classes 
which explicitly overload all their method calls so that they can only 
be sensibly used in markup. (e.g. methods like equals() and toString() 
would be intercepted, so these classes would be a little wierd to use).

Or should we just have an interface that the markup invokes in dynamic 
typing mode - then provide an implementation of the interface which 
delegates to methods.

Where possible I hope folks could just write regular POGOs (plain old 
groovy objects) and not have to do wacky stuff like intercept all method 

>> ...
>> Then we could use the swing markup example at the top of this mail, 
>> where SwingBuilder might not need to explicitly implement the Markup 
>> interface.
> Doesn't this lose the possibility of static checking on the 
> SwingBuilder?  (I.e., its invocations are forced through a Markup 
> interface which turns method names into strings, which renders them 
> uncheckable before runtime.)
> I still don't see why 'with' needs to know about any interface 
> whatsoever.  Help?

I guess there are a few different use cases here...

1) we need to do generic markup (e.g. an XML marshaller, accepting any 
XML) that takes any 'element name' - to implement this we could overload 
ever single possible method invocation on the builder - or we could have 
an interface to represent a 'generic' markup method call, passing the 
element name as a parameter (rather than using the method name as the 
element name. This interface is optional, its up to the builder to 
decide if its a 'generic markup' kinda guy, or just implements certain 
schemas of markup

2) we only wanna call static methods in markup and the builder is 
statically typed. In this case don't use a magic interface, just invoke 
the methods directly & generate a compile time error if the markup 
doesn't match the methods

3) same as above but the builder is dynamically typed. In this case we 
could try invoking the methods directly via dynamic method dispatch and 
throwing a runtime exception

I guess really this adapter thing is purely an implementation detail 
really - I was wondering if it'd allow us a single place to maybe 
isolate some specific markup -> method call mapping logic. So rather 
than us having to do complex stuff when expanding markup, the bytecode 
generated would always either...

* use static typed method invocations on a statically typed builder 
(after doing compile time checks for typeos)

* call the Markup interface (if the builder doesn't implement this 
interface - we'd create an adapter object to bind the Markup interface 
to direct method invocations on the builder object)

Did that help clear up what I was rambling on about before?

This markup can be a bit confusing at times as there's a few different 
use cases of static/dynamic typing and generic markup (XML processing 
stuff) versus specific markup (specific schemas, concreate APIs like