8.2. JDOM and FactoriesMoving right along, recall the discussion from the last chapter on JDOM and factories. I mentioned that you would never see code like this (at least with the current versions) in JDOM applications:
Well, that remains true. However, I glossed over some pretty important aspects of that discussion, and want to pick it up again here. As I mentioned in Chapter 7, "JDOM", being able to have some form of factories allows greater flexibility in how your XML is modeled in Java. Take a look at the simple subclass of JDOM's Element class shown in Example 8-1. Example 8-1. Subclassing the JDOM Element class
This is about as simple a subclass as you could come up with; it is somewhat similar to the NamespaceFilter class from Chapter 4, "Advanced SAX ". It disregards whatever namespace is actually supplied to the element (even if there isn't a namespace supplied!), and sets the element's namespace defined by the URI http://www.oreilly.com with the prefix ora.[12] This is a simple case, but it gives you an idea of what is possible, and serves as a good example for this section.
8.2.1. Creating a FactoryOnce you've got a custom subclass, the next step is actually using it. As I already mentioned, JDOM considers having to create all objects with factories a bit over-the-top. Simple element creation in JDOM works like this:
Things remain equally simple with a custom subclass:
The element is dropped into the O'Reilly namespace because of the custom subclass. Additionally, this method is more self-documenting than using a factory. It's clear at any point exactly what classes are being used to create objects. Compare that to this code fragment:
It's not clear if the object created is an Element instance, an ORAElement instance, or something else entirely. For these reasons, the custom class approach serves JDOM well. For object creation, you can simply instantiate your custom subclass directly. However, the need for factories arises when you are building a document: // Build from an input source SAXBuilder builder = new SAXBuilder( ); Document doc = builder.build(someInputStream); Obviously, here you were not able to specify custom classes through the building process. I suppose you could be really bold and modify the SAXBuilder class (and the related org.jdom.input.SAXHandler class), but that's a little ridiculous. So, to facilitate this, the JDOMFactory interface, in the org.jdom.input package, was introduced. This interface defines methods for every type of object creation (see Appendix A, "API Reference" for the complete set of methods). For example, there are four methods for element creation, which match up to the four constructors for the Element class: public Element element(String name); public Element element(String name, Namespace ns); public Element element(String name, String uri); public Element element(String name, String prefix, String uri); You will find similar methods for Document, Attribute, CDATA, and all the rest. By default, JDOM uses the org.jdom.input.DefaultJDOMFactory, which simply returns all of the core JDOM classes within these methods. However, you can easily subclass this implementation and provide your own factory methods. Look at Example 8-2, which defines a custom factory. Example 8-2. A custom JDOMFactory implementation
This is a simple implementation; it doesn't need to be very complex. It overrides each of the element( ) methods and returns an instance of the custom subclass, ORAElement, instead of the default JDOM Element class. At this point, any builder that uses this factory will end up with ORAElement instances in the created JDOM Document object, rather than the default Element instances you would normally see. All that's left is to let the build process know about this custom factory. 8.2.2. Building with Custom ClassesOnce you have a valid implementation of JDOMFactory, let your builders know to use it by invoking the setFactory( ) method and passing in a factory instance. This method is available on both of the current JDOM builders, SAXBuilder and DOMBuilder. To see it in action, check out Example 8-3. This simple class takes in an XML document and builds it using the ORAElement class and CustomJDOMFactory from Example 8-1 and Example 8-2. It then writes the document back out to a supplied output filename, so you can see the effect of the custom classes. Example 8-3. Building with custom classes using a custom factory
I ran this on the contents.xml file used throughout the first several chapters: bmclaugh@GANDALF $ java javaxml2.ElementChanger contents.xml newContents.xml This hummed along for a second, and then gave me a new document (newContents.xml). A portion of that new document is shown in Example 8-4. Example 8-4. Output fragment from contents.xml after ElementChanger
Each element is now in the O'Reilly namespace, prefixed and referencing the URI specified in the ORAElement class. Obviously, you can take this subclassing to a much higher degree of complexity. Common examples include adding specific attributes or even child elements to every element that comes through. Many developers have existing business interfaces, and define custom JDOM classes that extend the core JDOM classes and also implement these business-specific interfaces. Other developers have built "lightweight" subclasses that discard namespace information and maintain only the bare essentials, keeping documents small (albeit not XML-compliant in some cases). The only limitations are your own ideas in subclassing. Just remember to set up your own factory before building documents, so your new functionality is included.
Copyright © 2002 O'Reilly & Associates. All rights reserved. |
|