Document Object Model (DOM) Parser

When you use the DOM API to parse an XML document, a tree structure representing the XML document is built in memory. You can then analyze the nodes of the tree to discover the XML contents.

Building a DOM Tree

The mechanism for instantiating a DOM parser is very similar to that for a SAX parser. A new instance of a DocumentBuilderFactory is obtained that is used to create a new DocumentBuilder.

The parse() method is called on this DocumentBuilder object to return an object that conforms to the public Document interface. This object represents the XML document tree. The following code fragment creates a DOM parser and reads the XML document from a file supplied as a command-line argument:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File(argv[0]));

With the DocumentBuilder.parse() method, you are not restricted to reading XML only from a file; you can also use a constructed InputStream or read from a source defined by a URL.

The Document obtained form the parse() method is a subclass of org.w3c.dom.Node. To simplify processing of the DOM tree, all of the objects in the tree are either Node objects or objects of a sub class of Node.

There are a number of methods provided in the Document interface to access the nodes in the tree. These are listed in Table 16.8.

The normalize() method should always be used to put all text nodes into a form where there are no adjacent text nodes or empty text nodes. In this form, the DOM view better reflects the XML structure.

After parsing an XML document the DOM parser has built an in-memory representation of the document that will look something like Figure 16.2.

Figure 16.2. Diagram of the DOM tree.


The root of the DOM tree is obtained with the getDocumentElement() method.

Element root = document.getDocumentElement();

This method returns an Element, which is simply a subclass of Node that may have attributes associated with it. An element can be the parent of other elements.

There are a number of methods provided in the Document interface to access the nodes in the tree, some of which are listed in Table 16.8. These methods return either a Node or a NodeList (ordered collection of nodes).

Table 16.8. Document Interface Methods to Traverse a DOM Tree
Method NameDescription
getDocumentElement()Allows direct access to the root element of the document
getElementsByTagName(String)Returns a NodeList of all the elements with the given tag name in the order in which they are encountered in the tree
getChildNodes()A NodeList that contains all children of this node
getParentNode()The parent of this node
getFirstChild()The first child of this node
getLastChild()The last child of this node
getPreviousSibling()The node immediately preceding this node

In a simple DOM application the getChildNodes() method can be used to recursively traverse the DOM tree. The NodeList.getLength() method can then be used to find out the number of nodes in the NodeList.

NodeList children = node.getChildNodes();
int len = (children != null) ? children.getLength() : 0;

In addition to the tree traversal methods, the Node interface provides the following methods (among others) to investigate the contents of a node as in Table 16.9.

Table 16.9. Document Interface Methods to Inspect DOM Nodes
Method NameDescription
getAttributes()A NamedNodeMap containing the attributes of a node if it is an Element or null if it is not.
getNodeName()A string representing the name of this node (the tag).
getNodeType()A code representing the type of the underlying object. A node can be one of ELEMENT_NODE, ATTRIBUTE_NODE, TEXT_NODE, CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE, ENTITY_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_TYPE_NODE, DOCUMENT_FRAGMENT_NODE, NOTATION_NODE.
getNodeValue()A string representing the value of this node. If the node is a text node, the value will be the contents of the text node; for an attribute node, it will be the string assigned to the attribute. For most node types, there is no value and a call to this method will return null.
getNamespaceURI()The namespace URI of this node.
hasAttributes()Returns a boolean to indicate whether this node has any attributes.
hasChildNodes()Returns a boolean to indicate whether this node has any children.

Modifying a DOM Tree

We will now look at using the DOM API—to modify the contents or structure of the XML. Unlike SAX, DOM provides a number of methods that allow nodes to be added, deleted, changed, or replaced in the DOM tree. Table 16.10 summarizes these methods.

Table 16.10. Document Interface Methods to Inspect DOM Nodes
Method NameDescription
appendChild(Node newNode)Adds the new node to the end of the NodeList of children of this node.
cloneNode(boolean deep)Returns a duplicate of a node. The cloned node has no parent. If deep is true, the whole tree below this node is cloned; if false, only the node itself is cloned.
insertBefore(Node newNode, Node refNode)Inserts the newNode before the existing refNode.
removeChild(Node oldNode)Removes the oldNode from the list of children.
replaceChild(Node newNode, Node oldNode)Replaces the oldNode with newNode in the child NodeList.
setNodeValue(String nodeValue)Set the value of this node, depending on its type.
setPrefix(java.lang.Stringprefix)Set the namespace prefix of this node.

For example, the following code fragment simply creates a new customer element and appends it to the end of the XML document:

Node newNode = addXMLNode (document, "Customer", "Columbus");
Element root = document.getDocumentElement();
root.appendChild(newNode);

private static Node addXMLNode (Document document, String name, String text) {
  Element e = document.createElement(name);
  Text t = document.createTextNode(text);
  e.appendChild (t);
  return e;
}

The following XML element is added to the XML file that is read in by this example code:

<customer>Columbus</customer>

Outputting a DOM Tree

Having parsed or created an XML document in memory, a common requirement is to output the DOM tree. The javax.xml.transform class defines a transformer that can be used to output a DOM tree from memory. The following code shows how easy it is to take a DOM tree and output it to the screen:

TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(root), new StreamResult(System.out));

NOTE

In Day 17 you will see how to use XML Sylesheets with the transformer object to format the transformed output.


A Simple DOM Example

The WebDDBuilder example shown in Listing 16.10 is a simple program that creates a new Web Application deployment descriptor and adds a single <servlet> and <servlet-mapping> element to the tree before writing the updated DD. The Web Application DD was described on Day 12, “Servlets.”

Listing 16.10. WebDDBuilder.java
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import java.io.*;
import org.w3c.dom.*;
import java.util.*;

public class WebDDBuilder {

    public static void main(String argv[]) {
        int argCount = argv.length;
        if (argCount != 2) {
            System.err.println("Usage: WebDDBuilder servlet-class URL-mapping");
            System.exit(1);
        }
        String servletClass = argv[0];
        String URLPattern = argv[1];
        try {
            WebDDBuilder dd = new WebDDBuilder();
            dd.addServlet(servletClass, URLPattern);
            // output document
            dd.print(System.out);
        }
        catch (IllegalArgumentException ex) {
            System.err.println ("Invalid argument" + ex);
            ex.printStackTrace(System.out);
        }
    }

    private static final String SERVLET_VERSION = "2.4";
    private static final String XML_NAMESPACE =
        "http://java.sun.com/xml/ns/j2ee";
    private static final String XML_SCHEMA_INST =
        "http://www.w3.org/2001/XMLSchema-instance";
    private static final String XML_SCHEMA_LOC =
        "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd";

    private static final String SERVLET = "servlet";
    private static final String SERVLET_MAPPING = "servlet-mapping";
    private static final String SERVLET_NAME = "servlet-name";
    private static final String SERVLET_CLASS = "servlet-class";
    private static final String URL_PATTERN = "url-pattern";

    private static final String[] DD_ELEMENTS = {"icon", "display-name",
        "description", "distributable", "context-param", "filter",
        "filter-mapping", "listener", "servlet", "servlet-mapping",
        "session-config", "mimemapping", "welcome-file-list", "error-page",
        "taglib", "resource-env-ref", "resource-ref", "security-constraint",
        "login-config", "security-role", "env-entry",
        "ejb-ref", "ejb-local-ref" };

    private Document document;
    private Element root;
    private HashMap DDElements;

    public WebDDBuilder () {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.newDocument();
            root = document.createElement("web-app");
            root.setAttribute("version", SERVLET_VERSION);
            root.setAttribute("xmlns", XML_NAMESPACE);
            root.setAttribute("xmlns:xsi", XML_SCHEMA_INST);
            root.setAttribute("xsi:schemaLocation", XML_SCHEMA_LOC);
            DDElements = createDDMap(DD_ELEMENTS);
        }
        catch (ParserConfigurationException ex) {
           System.err.println ("Failed to create DOM document:" + ex);
        }
    }

    private void addServlet (String servletClass, String URLPattern) {

        //create the servlet name from the servlet class name
        // if fully qualified class name take just last part
        int index = servletClass.lastIndexOf(".");
        String servletName;
        if (index != -1)
            servletName = servletClass.substring(index+1);
        else
            servletName = servletClass;

        // build the servlet element
        Element servlet_name = document.createElement(SERVLET_NAME);
        servlet_name.appendChild(document.createTextNode(servletName));

        Element servlet_class = document.createElement(SERVLET_CLASS);
        servlet_class.appendChild(document.createTextNode(servletClass));

        Element servlet = document.createElement (SERVLET);
        servlet.appendChild(servlet_name);
        servlet.appendChild(servlet_class);

        // find where in the DOM to insert the new servlet node
        Node refChild = findNode (root, DDElements, SERVLET);
        root.insertBefore(servlet, refChild);

        // build the servlet-mapping element
        Element url_pattern = document.createElement(URL_PATTERN);
        url_pattern.appendChild(document.createTextNode(URLPattern));

        Element servlet_mapping = document.createElement (SERVLET_MAPPING);
        // no need to create servlet name element as we already have one
        // make sure we clone deep so that we get the text node
        servlet_mapping.appendChild(servlet_name.cloneNode(true));
        servlet_mapping.appendChild(url_pattern);

        refChild = findNode (root, DDElements, SERVLET_MAPPING);
        root.insertBefore(servlet_mapping, refChild);
    }

    private void print (PrintStream stream) {
        try {
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT,"yes");
            transformer.transform(new DOMSource(root),
                                  new StreamResult(stream));
        }
        catch (TransformerConfigurationException ex) {
            System.err.println ("Failed to create transformer factory:" + ex);
        }
        catch (TransformerException ex) {
            System.err.println ("Failed to transform DOM tree:" + ex);
        }
    }

    private Node findNode (Node treeRoot, HashMap ddSchema, String tagName) {

        // find out index of tagName
        int refKey = getKey (ddSchema, tagName);

        NodeList tags = treeRoot.getChildNodes();
        int tagsLen = (tags != null) ? tags.getLength() : 0;

        // find first tag after tagName in tree
        for (int i = 0; i < tagsLen; i++) {
            Node tag = tags.item(i);
            if (getKey(ddSchema, tag.getNodeName()) > refKey)
                return tag;
        }
        return null;
    }

    private int getKey (HashMap ddSchema, String tagName) {
        for (int key = 0; key < ddSchema.size(); key++) {
            if (ddSchema.get(new Integer(key)).equals(tagName))
                return key;
        }
        return -1;
    }

    private HashMap createDDMap(String[] ddSchema) {
        HashMap map = new HashMap();
        for (int i = 0; i < ddSchema.length; i++)
            map.put(new Integer(i), ddSchema[i]);
        return map;
    }

The WebDDBuilder example in Listing 16.10 starts by creating a new, empty, DOM tree representing an empty Web Application DD. Next the addServlet() method is called to add the servlet name and URL pattern passed as command-line parameters.

The addServlet()method builds two XML DD elements, <servlet> and <servlet-mapping>, using the values supplied as arguments. Each of these elements has a <servlet-name> sub-element, so instead of creating a new element from scratch, addServlet uses the Node.cloneNode() method to create a copy. A deep copy is preformed by passing true as the parameter to cloneNode; this ensures that all the child nodes are also cloned.

Finally, the print() method is called to output the DOM tree using a Transformer object.

As with the SAX example, this code does not use any J2EE components; you can simply compile and run it from the command line. From the Day16/examples directory run the command

> java –classpath classes demo.Hello /hello

This will create a DD entry for the demo.servlet with URL pattern /hello. Alternatively, use the supplied asant build files and enter

> asant WebDDBuilder

Provide the servlet name and URL pattern when prompted:

The resultant DD looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee
/web-app_2_4.xsd">
  <servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>demo.Hello</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.46.181