9.1. Creating a DOM Document

The DOM API provides an exhaustive collection of methods to create and modify a document. The most frequently used are:

builder.newDocument()
creates an empty document to which elements can be added
doc.createElement(String s)
creates a new element named s in document doc
parent.appendChild(Element e)
adds element e as the last child of element parent; if e was already the child of another node, it is removed before being assigned to its new parent
e.setAttribute(String name, String value)
adds the attribute name with the corresponding value to the element e; if the attribute already exists, its value is replaced
doc.createTextNode(String s)
creates a new text node with content s in document doc

In order to simplify parsing, we create a customized StreamTokenizer which returns a single token for all characters between separators used in the compact form (i.e. opening and closing brackets, at-sign and newline). The separators are also returned as a single token. The implementation of this tokenizer is given in Example 9.1.

In Example 9.2, the main method (line 24‑1) first creates a Document instance (line 29‑2) which will hold the XML tree. It then creates a specialized tokenizer (line 30‑3) from the file name given as argument to the program. It goes on to find the name of the root element (line 34‑4) and creates the root node. It then calls the expand method (line 39‑5) which add the whole content of the element a child of the document. To output the DOM structure, we create an identity transformation (line 41‑6) and use the document as source and System.out as output (line 46‑8). We also set an output property so that the output is nicely indented (line 43‑7).

Expansion (line 52‑9) is a recursive process that adds element and text nodes to a parent node received as a parameter; it processes each attribute (line 55‑11) by getting the name and value of the attribute and adding it to the current element line 58‑12; the content of the element (line 62‑13) is processed by looping until a closing bracket is encountered. When the next node is followed by an open bracket, a new child is created and expanded recursively (line 66‑14); otherwise, a text node is created (line 68‑15).

Example 9.1. [CompactTokenizer.java]: Specialized stream tokenizer that ignores blank tokens

  1 import  java.io.Reader;
    import  java.io.IOException;
    import  java.io.StreamTokenizer;
    
  5 public class CompactTokenizer {
        private StreamTokenizer st;
        
        CompactTokenizer(Reader r){                                      (1)
            st = new StreamTokenizer(r);
 10         st.resetSyntax(); // remove parsing of numbers...            (2)
            st.wordChars('\u0000','\u00FF'); // everything is part of a word
                                             // except the following...
            st.ordinaryChar('\n');
            st.ordinaryChar('[');
 15         st.ordinaryChar(']');
            st.ordinaryChar('@');
        }
    
        public String nextToken() throws IOException{                    (3)
 20         st.nextToken();
            while(st.ttype=='\n'|| 
                  (st.ttype==StreamTokenizer.TT_WORD && 
                   st.sval.trim().length()==0))
                st.nextToken();
 25         return getToken();
        }
    
        public String getToken(){                                        (4)
            return (st.ttype == StreamTokenizer.TT_WORD) ? st.sval : (""+(char)st.ttype);
 30     }
    
        public String skip(String sym) throws IOException {              (5)
            if(getToken().equals(sym))
                return nextToken();
 35         else
                throw new IllegalArgumentException("skip: "+sym+" expected but"+ 
                                                   sym +" found ");
        }
    }
 40 

1

Constructor that receives a Reader and creates a customized StreamTokenizer.

2

Because we do not want to have numbers and Java comments to be dealt with, we reset the syntax to indicate that all characters can be part of a word except for special separators used in the compact form.

3

Calls the Java tokenizer and skips newlines and empty text nodes. It returns the current token as a String.

4

Function for accessing the current token as a String.

5

Function for checking that the current token corresponds to the one given in parameter. Raises an exception if this is not the case. This is useful for checking the input that will not appear in the output corresponds to what is expected.


Example 9.2. [DOMExpand.java]: Compact form parsing to create a DOM XML document

A sample input for this program is Example 5.8 to give back Example 2.2.

  1 import  org.w3c.dom.Element;
    import  org.w3c.dom.Document;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
  5 
    import javax.xml.transform.OutputKeys;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.TransformerException;
 10 import javax.xml.transform.TransformerConfigurationException;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    
    
 15 import  java.io.IOException;
    import  java.io.BufferedReader;
    import  java.io.FileInputStream;
    import  java.io.InputStreamReader;
    import  java.io.StreamTokenizer;
 20 
    public class DOMExpand {
        static CompactTokenizer st; 
        
        public static void main( String[] argv ) {                       (1)
 25         try {
                DocumentBuilderFactory factory = 
                    DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.newDocument();                    (2)
 30             st = new CompactTokenizer(                               (3)
                        new BufferedReader(
                            new InputStreamReader(
                              new FileInputStream(argv[0]))));
                String rootName = "dummyElement";                        (4)
 35             // ignore everything preceding the word before the first "["
                while(!st.nextToken().equals("[")){
                    rootName=st.getToken();
                }
                expand((Element)doc.appendChild(doc.createElement(rootNam(5)e.trim())));  
 40             // output with an "identity" Transformer
                TransformerFactory tFactory = TransformerFactory.newInstance();(6)
                Transformer transformer = tFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT,"yes");  (7)
                DOMSource source = new DOMSource(doc);
 45             StreamResult result = new StreamResult(System.out);
                transformer.transform(source,result);                    (8)
            } catch ( Exception ex ) {
                ex.printStackTrace();
            }
 50     }
    
        static void expand(Element elem) throws IOException{             (9)
            Document doc = elem.getOwnerDocument();                      (10)
            st.skip("["); 
 55         while(st.getToken().equals("@")){// process attributes       (11)
                String attName = st.nextToken();
                st.nextToken();
                elem.setAttribute(attName,st.skip("["));                 (12)
                st.nextToken();
 60             st.skip("]");
            }
            while(!st.getToken().equals("]")){ // process element        (13)
                String s = st.getToken().trim();
                st.nextToken();
 65             if(st.getToken().equals("["))
                    expand((Element)elem.appendChild(doc.createElement(s)(14)));
                else{
                    elem.appendChild(doc.createTextNode(s));             (15)
                }
 70         }
            st.skip("]");
        }
    }
    

1

Main procedure for expanding a compact form.

2

Creates a new empty DOM document.

3

Creates a special purpose tokenizer, described in Example 9.1, to process the file whose name is given as a parameter of the program

4

Gets the identifier before the first opening bracket and uses it as the name for the root element inserted as a child of the document.

5

Expands the rest of the file as a child of the root just created.

6

Creates an identity transformer for serializing the output.

7

Makes the serializer pretty-print the output.

8

Creates a transformation source from the DOM structure created.

9

Recursive procedure for expanding the content of a file whose tokens are returned by st. The content is inserted as the child of the elem element.

10

Finds a reference to the document node necessary for creating new element and text nodes.

11

Loops on all attribute name and value pairs.

12

Adds the attribute to the current element.

13

Loops on all elements until the closing bracket.

14

Creates a new element with name s and adds it as a child of the current element. Recursively expands the next element as a child of the newly created child.

15

Adds a text node to the current element.