This post originated from an RSS feed registered with Java Buzz
by Chris Winters.
Original Post: Using a factory in commons-digester
Feed Title: cwinters.com
Feed URL: http://www.cwinters.com/search/registrar.php?domain=jroller.com®istrar=sedopark
Feed Description: Chris Winters on Java, programming and technology, usually in that order.
commons-digester is one of the more useful jakarta commons libraries. Yeah, it's verbose and it might take a little while to grok if you're not used to stack processing and other xml-isms. But once grokked it's applicable many places, especially since lots of systems are passing around XML these days. Parsing a message-based document (short) into an object seems to be pretty quick too and since you're using rules it's pretty easy to extend once you get the basics down -- this article has a practical application of it.
The idea behind the digester is that you have events that fire based on element paths. A common scenario: when you hit a path create a new object, then set its attributes and child elements as properties, something like:
// Process the address from a message like:
// <customer first="foo" last="bar">
// <address
// street="1234 Main Street" city="Wilmerding" state="PA" />
// </customer>
Digest d = new Digester();
...
// push a new object on the stack...
d.addObjectCreate( "customer/address", Address.class );
// this will call on the topmost stack object
// 'setStreet()', 'setCity()', 'setState()'
d.addSetProperties( "customer/address" );
// ...or you can do it explicitly
d.addSetProperties( "customer/address", "street", "street" );
d.addSetProperties( "customer/address", "city", "city" );
d.addSetProperties( "customer/address", "state", "state" );
// add the topmost stack object to the previous one
d.addSetNext( "customer/address" );
Verbose, but not so bad. What happens when you have different types of addresses?
Assuming e've got a parent class 'Address' and subclasses 'CanadaAddress', 'UnitedStatesAddress', etc. How do we tell digester to create the right object? A factory, of course:
// just change the 'addObjectCreate()' to:
d.addFactoryCreate( "customer/address", AddressFactory.class );
The factory class is cake:
// 'AbstractObjectCreationFactory' is from commons-digester
public class AddressFactory extends AbstractObjectCreationFactory
{
public Object createObject( Attributes attributes )
throws Exception
{
String country = attributes.getValue( "country);
if ( "USA".equals( country ) ) {
return new UnitedStatesAddress();
}
else if ( "Canada".equals( country ) ) {
return new CanadaAddress();
}
...