• No results found

Overloaded?

6.3 DOM Level 2 Modules

7.3.2 Loading XML

There's not much left to do here; basically, the class writes out to XML, provides access to XML (through the methods already existing on the Properties class), and now simply needs to read in XML. This is a fairly simple task; it boils down to more recursion. I'll show you the code modifications needed, and then walk you through them. Enter the code shown here into your XMLProperties.java source file:

public void load(Reader reader) throws IOException {

try {

// Load XML into JDOM Document SAXBuilder builder = new SAXBuilder( ); Document doc = builder.build(reader);

// Turn into properties objects

loadFromElements(doc.getRootElement().getChildren( ), new StringBuffer(""));

} catch (JDOMException e) {

throw new IOException(e.getMessage( )); }

}

private void loadFromElements(List elements, StringBuffer baseName) { // Iterate through each element

for (Iterator i = elements.iterator(); i.hasNext( ); ) { Element current = (Element)i.next( );

String name = current.getName( ); String text = current.getTextTrim( );

// Don't add "." if no baseName if (baseName.length( ) > 0) { baseName.append("."); } baseName.append(name);

// See if we have an element value if ((text == null) || (text.equals(""))) { // If no text, recurse on children

loadFromElements(current.getChildren( ), baseName);

} else {

// If text, this is a property

setProperty(baseName.toString( ), text);

}

// On unwind from recursion, remove last name if (baseName.length() == name.length( )) { baseName.setLength(0); } else { baseName.setLength(baseName.length( ) - (name.length( ) + 1)); } } }

The implementation of the load( ) method (which all overloaded versions delegate to) uses SAXBuilder to read in the supplied XML document. I discussed this earlier in the chapter, and I'll look at it in even more detail in the next; for now, it's enough to realize that it simply reads XML into a JDOM Document object.

The name for a property consists of the names of all the elements leading to the property value, with a period separating each name. Here's a sample property in XML:

<properties> <org> <enhydra> <classpath>"."</classpath> </enhydra> </org> </properties>

The property name can be obtained by taking the element names leading to the value (excluding the properties element, which was used as a root-level container): org,

enhydra, and classpath. Throw a period between each, and you get

org.enhydra.classpath, which is the property name in question. To accomplish this, I coded up the loadFromElements( ) method. This takes in a list of elements, iterates through them, and deals with each element individually. If the element has a textual value, that value is added to the superclass object's properties. If it has child elements instead, then the children are obtained, and recursion begins again on the new list of children. At each step of recursion, the name of the element being dealt with is appended to the

baseName variable, which keeps track of the property names. Winding through recursion,

baseName would be org, then org.enhydra, then org.enhydra.classpath. And, as recursion unwinds, the baseName variable is shortened to remove the last element name. Let's look at the JDOM method calls that make it possible.

First, you'll notice several invocations of the getChildren( ) method on instances of the

Element class. This method returns all child elements of the current element as a Java

for, and return either all elements with that name (getChildren(String name)), or just the first child element with that name (getChild(String name)). There are also namespace-aware versions of the method, which will be covered in the next chapter. To start the recursion process, the root element is obtained from the JDOM Document object through the getRootElement( ) method, and then its children are used to seed

recursion. Once in the loadFromElements( ) method, standard Java classes are used to move through the list of elements (such as java.util.Iterator). To check for textual content, the getTextTrim( ) method is used. This method returns the textual content of an element, and returns the element without surrounding whitespace.[5] Thus, the content " textual content " (note the surrounding whitespace) would be returned as "textual content". While this seems somewhat trivial, consider this more realistic example of XML:

[5] It also removes more than one space between words. The textual content "lots of spaces" would be

returned through getTextTrim( ) as "lots of spaces". <chapter>

<title>

Advanced SAX </title>

</chapter>

The actual textual content of the title element turns out to be several spaces, followed by a line feed, followed by more space, the characters "Advanced SAX", more space, another line feed, and even more space. In other words, probably not what you expected. The returned string data from a call to getTextTrim( ) would simply be "Advanced SAX", which is what you want in most cases anyway. However, if you do want the complete content (often used for reproducing the input document exactly as it came in), you can use the getText( ) method, which returns the element's content unchanged. If there is no content, the return value from this method is an empty string (""), which makes for an easy comparison, as shown in the example code. And that's about it: a few simple method calls and the code is reading XML with JDOM. Let's see this class in action.