package org.tigris.juxy.util;
import org.w3c.dom.Node;
import java.util.Iterator;
/**
* Iterator over DOM. This iterator iterates DOM elements in the
* order of their appearance in the document: it first processess all child elements
* down the DOM tree, then following sibling nodes on each level.
* This is the same as iterate over results of the <code>//</code> XPath expression.
*
* @author Pavel Sher
*/
public class DOMIterator implements Iterator {
private Node rootNode;
private Node currentNode;
private Node[] cache = new Node[]{null, null};
public DOMIterator(Node rootNode) {
if (rootNode == null)
throw new IllegalArgumentException("rootNode must not be null");
this.rootNode = rootNode;
currentNode = rootNode;
}
/**
* Not supported.
*/
public void remove() {
throw new UnsupportedOperationException("remove() is not supported");
}
public boolean hasNext() {
if (currentNode.hasChildNodes())
return true;
return currentNode.getNextSibling() != null || getNodeWithSibling(currentNode) != null;
}
public Object next() {
Node nextNode = getChildOrSiblingNode(currentNode);
if (nextNode != null) {
currentNode = nextNode;
return currentNode;
}
// if there are no child and sibling nodes and current node is not
// child of root node then move up
Node nodeWithSibling = getNodeWithSibling(currentNode);
if (nodeWithSibling != null) {
currentNode = nodeWithSibling.getNextSibling();
return currentNode;
}
return null;
}
/**
* Traverses up to the root node and checks whether
* current node has sibling nodes. Traverse stops when the first node
* with siblings found or current node reaches rootNode.
* Result of a search will be stored in the cache and if will be returned for all
* subsequent callings with the same parameter.
*
* @param fromNode node from which traverse is performed
* @return node having sibling nodes or null if such node was not found
*/
private Node getNodeWithSibling(Node fromNode) {
if (cache[0] == fromNode)
return cache[1];
if (fromNode == rootNode)
return null;
Node parent = fromNode.getParentNode();
Node nodeWithSibling = null;
while (parent != null && parent != rootNode) {
if (parent.getNextSibling() != null) {
nodeWithSibling = parent;
break;
}
parent = parent.getParentNode();
}
cache[0] = currentNode;
cache[1] = nodeWithSibling;
return nodeWithSibling;
}
private Node getChildOrSiblingNode(Node fromNode) {
// if current node has child nodes then return first child
if (fromNode.hasChildNodes())
return fromNode.getFirstChild();
// if there are no child elements return first sibling element
return fromNode.getNextSibling();
}
}