- Simple API For XML Version 2 (SAX2)
- Auxiliary SAX Interfaces
- SAX and I/O
- SAX Error Handling
- The Glue of SAX: XMLReader
- The Document Object Model
- The Object Model
- The DOM and Factories
- The Node Interface
- Parents and Children
- Nonhierarchical Nodes
- Text Nodes
- Element and Attribute Nodes
- Document, Document Type, and Entity Nodes
- Bulk Insertion Using Document Fragment
- DOM Error Handling
- Implementation vs Interface
- DOM Traversal
- Where Are We?
The DOM and Factories
The DOM relies on a fairly stylized resource management strategy. Because the DOM consists solely of abstract interfaces and has no concrete classes, there is no class one can simply call new on to create new nodes. Rather, the DOM expects that all objects (except for one) are created indirectly via well-known factory methods that are exposed on several DOM interfaces. Of course, this still leaves a bootstrapping problem, as one must have an object reference in order to call a DOM-specified factory method. This is where the DOMImplementation interface comes in.
Each implementation of the DOM interfaces must provide an object that implements DOMImplementation. This object acts as a rendezvous point for all component-wide functionality. The DOMImplementation interface is defined as follows:
interface DOMImplementation { boolean hasFeature(in DOMString feature, in DOMString version); DocumentType createDocumentType( in DOMString qualifiedName, in DOMString publicId, in DOMString systemId) raises(DOMException); Document createDocument( in DOMString namespaceURI, in DOMString qualifiedName, in DocumentType doctype) raises(DOMException); };
How one acquires an initial reference to this interface is proprietary to each implementation. The following illustrates the Apache xerces-specific technique:
org.w3c.dom.DOMImplementation loadDOM() { return new org.apache.xerces.dom.DOMImplementationImpl(); }
Note that in Java, all core DOM interfaces are in the org.w3c.dom package.
Given an implementation of DOMImplementation, one can create nodes of type Document and of type DocumentType (which corresponds to the DTD of a document information item). Consider the following Java code:
org.w3c.dom.Document create() { org.w3c.dom.DOMImplementation impl = loadDOM(); org.w3c.dom.DocumentType dtd = impl.createDocumentType( "foo:bar", "-//FooBar//", "foo.dtd"); return impl.createDocument("http://foo.com/schema/", "foo:bar", dtd); }
This function returns a Document node that corresponds to the following serialized document:
<?xml version='1.0' ?> <!DOCTYPE foo:bar PUBLIC "-//FooBar//" "foo.dtd"> <foo:bar xmlns:foo="http://foo.com/schema/" />
Note that at the time of this writing, the W3C DOM specification provided no standard mechanism for translating a DOM Document to or from a serialized XML document. However, most implementations provide a proprietary mechanism for doing this.
The DOMImplementation interface provides factory methods for Document and DocumentType nodes. The factory methods for most of the remaining node types are on the Document interface itself.
interface Document : Node { Element createElementNS(in DOMString namespaceURI, in DOMString qualifiedName);8 Attr createAttributeNS(in DOMString namespaceURI, in DOMString qualifiedName); DocumentFragment createDocumentFragment(); Text createTextNode(in DOMString data); Comment createComment(in DOMString data); CDATASection createCDATASection(in DOMString data); ProcessingInstruction createProcessingInstruction( in DOMString target, in DOMString data); EntityReference createEntityReference( in DOMString name); Node importNode(in Node importedNode, in boolean deep); // remaining methods elided for clarity };
As an example, this Java code
void addComment(org.w3c.dom.Document document) { org.w3c.dom.Comment comment; comment = document.createComment("Hello, world"); document.appendChild(comment); }
would append a comment node containing the text "Hello, world" to the given document.
When a new node is created by a Document, that node can only be inserted into that document. That means that the results of running the following code are undefined:
void evilCode(Document doc1, Document doc2) { Node node = doc1.createComment("Hello, world"); doc2.appendChild(node); // this will fail! }
To support using nodes created by foreign Document objects, the Document interface provides importNode method.
Node importNode(in Node importedNode, in boolean deep);
This method should be called on the target Document that will accept the node into its hierarchy. The following is the correct version of the previous illegal code fragment:
void niceCode(Document doc1, Document doc2) { Node node = doc1.createComment("Hello, world"); Node safe = doc2.importNode(node, true); doc2.appendChild(safe); // this will succeed! }
Document.importNode is the only method in the DOM core that accepts a cross-document node reference.
It is always possible to find the Document with which a node is associated via the Node.ownerDocument attribute.
interface Node { readonly attribute Document ownerDocument; : : :
Similarly, it is always possible to find the DOMImplementation with which a Document node is associated via the Document.implementation attribute.
interface Node { readonly attribute DOMImplementation implementation; : : :
These two attributes ensure that given any node in a document, one can safely create related nodes using the corresponding Document and DOM Implementation objects.