Special-Purpose Factories
In addition to the default DocumentFactory used throughout this chapter, dom4j includes some subclasses of DocumentFactory that both demonstrate the rationale behind dom4j's interface-based design and provide useful functionality. This is not an exhaustive look at all the DocumentFactory classes in the dom4j distribution. For a full list, please take a look at the dom4j Javadocs at http://www.dom4j.org/apidocs. In general, these factories can be used when creating new dom4j objects directly or with a builder object. To use a DocumentFactory subclass with a builder, either pass the DocumentFactory to the builder's constructor or its setDocumentFactory( ) method:
// create a SAXBuilder with DOMDocumentFactory as it's factory SAXBuilder builder = new SAXBuilder(DOMDocumentFactory.getInstance( )); // parse something // now switch the factory to BeanDocumentFactory builder.setDocumentFactory(BeanDocumentFactory.getInstance( ));
DOMDocumentFactory
The factory org.dom4j.dom.DOMDocumentFactory and the classes it produces are perhaps the best case for the interface-based design used by both DOM and dom4j. The instances produced by DOMDocumentFactory implement the corresponding interfaces from both dom4j and W3C DOMthe result of a call to createDocument( ) implements both org.dom4j.Document and org.w3c.dom.Document; createElement( ) returns an object that implements both org.dom4j.Element and org.w3c.dom.Element ; and so on. This is useful when working with classes that use the DOM interfaces such as if you had created a dom4j Element and wanted to pass it to the following interface:
public interface ElementProcessor { void doSomething(org.w3c.dom.Element element); }
You could use the DOMWriter class to create copies of your objects that implement the DOM interfaces. But using DOMDocumentFactory allows you to simply pass your Element object to the doSomething( ) method, by casting it to the org.w3c.dom.Element interface:
public org.dom4j.Element create(String name, ElementProcessor processor) { DocumentFactory factory = DOMDocumentFactory.getInstance( ); Element element = factory.createElement(name); processor.doSomething((org.w3c.dom.Element) element); return element; }
IndexedDocumentFactory
The factory org.dom4j.util.IndexedDocumentFactory creates instances of org.dom4j.util.IndexedElement, an implementation of the Element interface that has optimized versions of the attribute(String), attribute(QName), element(String), element(QName), elements(String), and elements(QName) methods. As child nodes are added to an IndexedElement, mappings from name (either as a String or QName) to child Elements and Attributes are built. In the default implementation of the Element interface, a call to any of these methods actually results in iteration through the list of attributes or elements until a match is found. IndexedElement is slower to add new child nodes, due to the need to add entries to the internal Maps, but it is definitely advantageous if you do a lot of name-based access.
BeanDocumentFactory
The factory org.dom4j.bean.BeanDocumentFactory creates JavaBean objects based on attributes within Element objects. These objects are made available through the getData( ) method of the Element interface. The way this works is that when an Element is created with an attribute named class, a new object is created with that attribute's value as its class name. If no such class exists, a warning is logged to the console. Once the object is created, the other attributes are set as property values on the bean. For example, the following element:
<book class="javaxml3.bookbean" pubDate="2004" title="XML in a Nutshell" />
if parsed by a builder using BeanDocumentFactory, produces an instance of javaxml3.BookBean. If they exist, methods called setPubDate( ) and setTitle( ) are called with the corresponding attribute value. If no such methods exist, no warning is logged. BeanDocumentFactory will not set bean properties based on child elements; this XML would only result in a call to setPubDate( ), not setTitle( ):
<book class="javaxml3.bookbean" pubDate="2003"> <title>Learning UML</title> </book>
Unlike DOMDocumentFactory and IndexedDocumentFactory, BeanDocumentFactory is really only useful when parsing an XML document. You can use it when building a Document in your code, but the bean objects will not be created. These three examples of DocumentFactory subclasses should give you a good idea of what you can do with a custom DocumentFactory. If you come up with a clever DocumentFactory subclass, feel free to submit it to the dom4j community via the mailing list. If it is generally useful, it may be included in a future version of dom4j. This chapter has looked at the broad feature set of dom4j and is the last of the object model XML APIs we discuss in this tutorial. In the next chapter, we will shift gears to discuss a technique for dealing with XML called data binding that removes just about every reference to XML from your code.