About the XML DOM Object Classes
The IDL XML DOM support is provided by a set of IDL object classes, all starting with IDLffXMLDOM. These classes provide access to the XML document via the DOM. The IDLffXMLDOM objects do not in themselves maintain a copy of the document data. Instead, they provide access to the data stored in the DOM document structure.
IDLffXMLDOMNode Class Hierarchy
One of the key object classes is IDLffXMLDOMNode. Because it is an abstract class, you will never create an instance of this class. The node is the basic DOM data structure used to map each DOM data element. The nodes are organized in a classic tree structure, according to the layout of the data in the document.
The following classes are derived from IDLffXMLDOMNode, where each class is named IDLffXML<node type> (e.g., IDLffXMLDOMAttr):
These classes represent the data that can be stored in an XML document. Except for the IDLffXMLDOMDocument class, you do not instantiate any of them directly. To begin working with the IDL XML DOM interface, you use the OBJ_NEW function to create an IDLffXMLDOMDocument object. You then use this object to browse and modify the document. This document object also creates objects using the derived classes to give you access to the various parts of the document.
For example:
creates an IDL object of one of the node types, depending on what the first child in your document actually is. The newly created IDL object refers to the first child node of the document and does not modify the document in any way.
You then use the oChild object's methods to get data from the node, modify the node, or find another node.
Because of the class hierarchy, all the methods in a superclass are available to its subclasses. For example, to determine which methods are available for use by an object of the IDLffXMLDOMText class, you would have to look at the methods belonging to the IDLffXMLDOMText, IDLffXMLDOMCharacterData, and IDLffXMLDOMNode classes.
Note
The IDLffXMLDOMCharacterData class is a special abstract class that provides character-handling facilities for its subclasses. You will never create an instance of this class.
IDLffXMLDOM Object Helper Classes
IDL provides a set of other classes to assist you in navigating the DOM tree. These classes are:
- IDLffXMLDOMNodeIterator — navigates in a depth-first, document-order traversal.
- IDLffXMLDOMTreeWalker — navigates in a tree-walking traversal.
- IDLffXMLDOMNodeList — contains a list of children of a node. You can create node lists using the GetElementsByTagName and GetChildNodes methods, for example.
- IDLffXMLDOMNamedNodeMap — contains a list of attributes from an element node that are looked up by attribute name.
The IDLffXMLDOMNodeIterator and IDLffXMLDOMTreeWalker classes do not contain lists used in tree traversal. Instead, they each operate by creating a node object for accessing a DOM node and then destroying that node object as the iterator or walker is moved to another DOM node. Conceptually, both node iterators and tree walkers are "current" node pointers into the DOM tree. For more information, see the classes' respective documentation in the IDL Reference Guide.
The IDLffXMLDOMNodeList and IDLffXMLDOMNamedNodeMap classes contain nodes that are subclasses of IDLffXMLDOMNode. Node lists and named node maps are active collections of nodes that are updated as the DOM tree is modified. That is, they are not static snapshots of a DOM tree in a given state; the list contents are modified as the DOM tree is modified. While this dynamic update is useful because you do not have to take specific action to update a list after modifying the tree, it can be confusing in some situations.
Suppose you want to delete all the children of an element node. The following code seems to make sense:
oList = oElement->GetChildNodes() n = oList->GetLength() FOR i=0, n-1 DO $ oDeleted = oElement->RemoveChild(oList->Item(i))
This approach does not work as expected because after the first child is deleted, the list is updated so it contains one fewer object, and the indexes of all remaining objects are decremented by one. As the loop continues, some items are not deleted, and eventually an error occurs when the loop index i exceeds the length of the shortened list.
The following code performs the intended deletion, by changing the parameter to the Item method from i to 0:
oList = oElement->GetChildNodes() n = oList->GetLength() FOR i=0, n-1 DO $ oDeleted = oElement->RemoveChild(oList->Item(0))
This code works because each time the first child is deleted, the list is automatically updated to place another object in the first position.
The following approach might be more appealing:
oList = oElement->GetChildNodes() n = oList->GetLength() FOR i=n-1, 0, -1 DO $ oDeleted = oElement->RemoveChild(oList->Item(i))
This code works because it deletes items from the end of the list, rather than from the beginning.
IDL Node Ownership
Whenever you create an IDLffXMLDOM node object with a method such as IDLffXMLDOMNode::GetFirstChild, you are also creating an ownership relationship between the created node object and the node object that created it.
Working from the previous plug-in example (see About the DOM Structure), suppose that you have an object reference, oName, to an instance of the IDLffXMLDOMElement class that refers to the first child of the plug-in node:
Using oName, you can issue the following call:
The description and name DOM nodes are siblings of each other in the DOM tree, as shown in Figure 21-2. The IDL object oDescription refers to the description node in the DOM tree, and the IDL object oName refers to the name node in the DOM tree. However, the oDescription object is owned by the oName object because oName created oDescription.
You might understand this relationship better by realizing that the parent/sibling relationships in the DOM tree reflect the DOM tree structure and that the ownership relationships among the IDL access objects are due to the creation of the IDL access objects. Because oName created oDescription, oName destroys oDescription when oName is destroyed, even though they refer to siblings in the DOM tree. Bear in mind that destroying these access objects does not affect the DOM tree itself.
This parent relationship among IDLffXMLDOM objects is useful for cleaning them up. Because all of the objects that might have been created during the exploration of a DOM tree are all ultimately descendants of an IDLffXMLDOMDocument node, simply destroying the document object is sufficient to clean up all the nodes. Unless you are concerned with cleaning up some access objects at a particular time (to save memory, for example), you can simply wait to clean them all up when you are finished with the data by destroying the IDLffXMLDOMDocument node.
To reduce memory requirements, you can destroy node objects that are no longer needed. For example, if you wanted to explore all the children of a given element oElement, you might use the following code:
oFirstChild = oElement->GetFirstChild() oChild = oFirstChild WHILE OBJ_VALID(oChild) DO BEGIN PRINT, oChild->GetNodeValue() oChild = oChild->GetNextSibling() ENDWHILE OBJ_DESTROY, oFirstChild
This approach works well because all the node objects created during the exploration of the children by the GetNextSibling method are destroyed when oFirstChild is destroyed. While it would seem that objects "lost" to the reassignment of oChild would not be accessible for destruction, the chain of oChild objects keeps track of them and destroys them all when the head of the chain, saved in oFirstChild, is destroyed.
Trying to destroy node objects inside the loop as follows does not work as expected:
oChild = oElement->GetFirstChild() WHILE OBJ_VALID(oChild) DO BEGIN PRINT, oChild->GetNodeValue() oNext = oChild->GetNextSibling() OBJ_DESTROY, oChild OChild = oNext ENDWHILE
This code fails because when oChild is destroyed for the first time, it also destroys oNext, causing the loop to exit after the first iteration.
If there is a very large number of children, waiting until the end of the loop to destroy the list might be too inefficient. Using a node list, as in the following code, is an alternative:
oList = oElement->GetChildNodes() n = oList->GetLength() FOR i=0, n-1 DO BEGIN oChild = oList->Item(i) PRINT, oChild->GetNodeValue() OBJ_DESTROY, oChild ENDFOR OBJ_DESTROY, oList
Although oList requires some space to maintain the list, there is only one valid node connected to oChild in memory each time through the loop.
You can change the node deletion policy so that nodes created by a node are not deleted when the node is destroyed. This change lets the following code work properly:
oDocument->SetProperty, NODE_DESTRUCTION_POLICY=1 oChild = oElement->GetFirstChild() WHILE OBJ_VALID(oChild) DO BEGIN PRINT, oChild->GetNodeValue() oNext = oChild->GetNextSibling() OBJ_DESTROY, oChild oChild = oNext ENDWHILE
Now, the OBJ_DESTROY call no longer destroys the object to which oNext refers, and the loop proceeds as expected.
Saving and Restoring IDLffXMLDOM Objects
IDL does not save IDLffXMLDOM objects in a SAVE file. If you restore a SAVE file that contains object references to IDLffXMLDOM objects, the object references are restored, but are set to null object references.
The IDLffXMLDOM objects are not saved because they contain state information for the external Xerces library. This state information is not available to IDL and cannot be restored. The contents of the XML file might also have changed, which would also make any saved state invalid.
It is recommended that applications either complete any DOM operations before saving their data in a SAVE file or reload the DOM document as part of restoring their state.
