DOM has a set of troublesome spots just like SAX, and just like the APIs we'll cover in the next few chapters. I will point some of those out to you, and hopefully save you a few hours of debugging time along the way. Enjoy; these happen to be problems that I've run into and struggled against for quite a while before getting things figured out.
The number one problem that I see among DOM developers is what I refer to as "the dreaded WRONG DOCUMENT exception." This exception occurs when you try to mix nodes from different documents. It most often shows up when you try to move a node from one document to another, which turns out to be a common task.
The problem arises because of the factory approach I mentioned earlier. Because each element, attribute, processing instruction, and so on is created from a Document instance, it is not safe to assume that those nodes are compatible with other Document instances; two instances of Document may be from different vendors with different supported features, and trying to mix and match nodes from one with nodes from the other can result in implementation-dependent problems. As a result, to use a node from a different document requires passing that node into the target document's insertNode( ) method. The result of this method is a new Node, which is compatible with the target document. In other words, this code is going to cause problems:
Element otherDocElement = otherDoc.getDocumentElement( ); Element thisDocElement = thisDoc.getDocumentElement( ); // Here's the problem - mixing nodes from different documents thisDocElement.appendChild(otherDocElement);
This exception will result:
org.apache.xerces.dom.DOMExceptionImpl: DOM005 Wrong document at org.apache.xerces.dom.ChildAndParentNode.internalInsertBefore( ChildAndParentNode.java:314) at org.apache.xerces.dom.ChildAndParentNode.insertBefore( ChildAndParentNode.java:296) at org.apache.xerces.dom.NodeImpl.appendChild(NodeImpl.java:213) at MoveNode.main(MoveNode.java:30)
To avoid this, you must first import the desired node into the new document:
Element otherDocElement = otherDoc.getDocumentElement( ); Element thisDocElement = thisDoc.getDocumentElement( ); // Import the node into the right document Element readyToUseElement = (Element)thisDoc.importNode(otherDocElement); // Now this works thisDocElement.appendChild(readyToUseElement);
Note that the result of importNode( ) is a Node, so it must be cast to the correct interface (Element in this case). Save yourself some time and effort and commit this to memory; write it on a notecard and tuck it under your pillow. Trust me, this is about the most annoying exception known to man!
Fixing the problem I just described often leads to another problem. A common error I've seen is when developers remember to import a node, and then forget to append it! In other words, code crops up looking like this:
Element otherDocElement = otherDoc.getDocumentElement( ); Element thisDocElement = thisDoc.getDocumentElement( ); // Import the node into the right document Element readyToUseElement = (Element)thisDoc.importNode(otherDocElement); // The node never gets appended!!
In this case, you have an element that belongs to the target document, but that never gets appended, or prepended, to anything within the document. The result is another tough-to-find bug, in that the document owns the element but the element is not in the actual DOM tree. Output ends up being completely devoid of the imported node, which can be quite frustrating. Watch out!
Copyright © 2002 O'Reilly & Associates. All rights reserved.