SwingBuilder is one of the most used Groovy builders. It follows the standard structure of BuilderSupport but uses the concept of factories to build each node. Seeing that the concept was useful enough for other builders the basic implementation was taken out of SwingBuilder and FactoryBuilderSupport was born (and SwingBuilder was retrofitted of course). How Builders Work
The Factory interface is the basic building block, the builder will call the factory's methods at specific points during node building, let's see them in their invocation order:
- Object newInstance( FactoryBuilderSupport builder, Object name, Object value, Map attributes ) throws InstantiationException, IllegalAccessException
Responsible for creating the object that responds to the node 'name' and its called during builder.createNode
- boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object node, Map attributes )
Gives the factory the ability to process the attributes as it may see fit with the option of stopping the builder to process them itself (by returning true).
- void setParent( FactoryBuilderSupport builder, Object parent, Object child )
void setChild( FactoryBuilderSupport builder, Object parent, Object child )
allows the factory to setup parent/child relationships.
- boolean isLeaf()
Lets the builder know if the node allows for further nodes to be nested on the current node.
- void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node )
Is the last method called from the factories perspective, it will let you handle any cleanup the node may require.
But that's not everything FactoryBuilderSupport has to offer. The factories may require contextual information on the current node being built to do its work, onNodeCompleted may require information that it is only available when newInstance is invoked, or newInstance may need to inspect the parent to decide what is the best way to create the node, just to mention a few scenarios, that's why FactoryBuilderSupport enables the following helping methods:
returns the context of the current node*
returns the context of the parent of the current node**
returns the factory that built the current node
returns the factory of the parent of the current node (if any)
returns the parent of the current node (if any)**
returns the current node*
* Note: In the newInstance(...) method, since the "current" node has not yet been created, getCurrent() and getContext() will return the parent node or parent context of the node that is currently being constructed.
** Note: In the newInstance(...) method, since the "current" node has not yet been created, getParentNode() and getParentContext() will return the grandparent node or grandparent context of the node that is currently being constructed.
The builder is marked as abstract so you are required to create a subclass for your own builders, despite that it doesn't enforce the implementation of any method at all. There are a couple of protected methods though, that when overwritten will give you more control over the builder's internal workings:
- Factory resolveFactory( Object name, Map attributes, Object value )
Usually what you would like in a subclass of FactoryBuilderSupport is a 1 to 1 relation on node names to factories, but for those cases where you would like n to 1 you can override this method and plug in your custom selection mechanism.
- void preInstantiate( Object name, Map attributes, Object value )
void postInstantiate( Object name, Map attributes, Object node )
void handleNodeAttributes( Object node, Map attributes )
Object postNodeCompletion( Object parent, Object node )
These methods are called during the lifecycle of a node, you can override them at any time but there is also a way to extend the behavior associated with those calls without overwriting the methods: you may register a closure, in fact as many as you like, to hook your own logic. The closures will be called from last to first as they were registered.
TODO document build() methods