| /* |
| * reserved comment block |
| * DO NOT REMOVE OR ALTER! |
| */ |
| /* |
| * Copyright 1999-2002,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.sun.org.apache.xerces.internal.dom; |
| |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.traversal.NodeFilter; |
| import org.w3c.dom.traversal.NodeIterator; |
| |
| |
| /** DefaultNodeIterator implements a NodeIterator, which iterates a |
| * DOM tree in the expected depth first way. |
| * |
| * <p>The whatToShow and filter functionality is implemented as expected. |
| * |
| * <p>This class also has method removeNode to enable iterator "fix-up" |
| * on DOM remove. It is expected that the DOM implementation call removeNode |
| * right before the actual DOM transformation. If not called by the DOM, |
| * the client could call it before doing the removal. |
| * |
| * @xerces.internal |
| * |
| */ |
| public class NodeIteratorImpl implements NodeIterator { |
| |
| // |
| // Data |
| // |
| |
| /** The DocumentImpl which created this iterator, so it can be detached. */ |
| private DocumentImpl fDocument; |
| /** The root. */ |
| private Node fRoot; |
| /** The whatToShow mask. */ |
| private int fWhatToShow = NodeFilter.SHOW_ALL; |
| /** The NodeFilter reference. */ |
| private NodeFilter fNodeFilter; |
| /** If detach is called, the fDetach flag is true, otherwise flase. */ |
| private boolean fDetach = false; |
| |
| // |
| // Iterator state - current node and direction. |
| // |
| // Note: The current node and direction are sufficient to implement |
| // the desired behaviour of the current pointer being _between_ |
| // two nodes. The fCurrentNode is actually the last node returned, |
| // and the |
| // direction is whether the pointer is in front or behind this node. |
| // (usually akin to whether the node was returned via nextNode()) |
| // (eg fForward = true) or previousNode() (eg fForward = false). |
| // Note also, if removing a Node, the fCurrentNode |
| // can be placed on a Node which would not pass filters. |
| |
| /** The last Node returned. */ |
| private Node fCurrentNode; |
| |
| /** The direction of the iterator on the fCurrentNode. |
| * <pre> |
| * nextNode() == fForward = true; |
| * previousNode() == fForward = false; |
| * </pre> |
| */ |
| private boolean fForward = true; |
| |
| /** When TRUE, the children of entites references are returned in the iterator. */ |
| private boolean fEntityReferenceExpansion; |
| |
| // |
| // Constructor |
| // |
| |
| /** Public constructor */ |
| public NodeIteratorImpl( DocumentImpl document, |
| Node root, |
| int whatToShow, |
| NodeFilter nodeFilter, |
| boolean entityReferenceExpansion) { |
| fDocument = document; |
| fRoot = root; |
| fCurrentNode = null; |
| fWhatToShow = whatToShow; |
| fNodeFilter = nodeFilter; |
| fEntityReferenceExpansion = entityReferenceExpansion; |
| } |
| |
| public Node getRoot() { |
| return fRoot; |
| } |
| |
| // Implementation Note: Note that the iterator looks at whatToShow |
| // and filter values at each call, and therefore one _could_ add |
| // setters for these values and alter them while iterating! |
| |
| /** Return the whatToShow value */ |
| public int getWhatToShow() { |
| return fWhatToShow; |
| } |
| |
| /** Return the filter */ |
| public NodeFilter getFilter() { |
| return fNodeFilter; |
| } |
| |
| /** Return whether children entity references are included in the iterator. */ |
| public boolean getExpandEntityReferences() { |
| return fEntityReferenceExpansion; |
| } |
| |
| /** Return the next Node in the Iterator. The node is the next node in |
| * depth-first order which also passes the filter, and whatToShow. |
| * If there is no next node which passes these criteria, then return null. |
| */ |
| public Node nextNode() { |
| |
| if( fDetach) { |
| throw new DOMException( |
| DOMException.INVALID_STATE_ERR, |
| DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null)); |
| } |
| |
| // if root is null there is no next node. |
| if (fRoot == null) return null; |
| |
| Node nextNode = fCurrentNode; |
| boolean accepted = false; // the next node has not been accepted. |
| |
| accepted_loop: |
| while (!accepted) { |
| |
| // if last direction is not forward, repeat node. |
| if (!fForward && nextNode!=null) { |
| //System.out.println("nextNode():!fForward:"+fCurrentNode.getNodeName()); |
| nextNode = fCurrentNode; |
| } else { |
| // else get the next node via depth-first |
| if (!fEntityReferenceExpansion |
| && nextNode != null |
| && nextNode.getNodeType() == Node.ENTITY_REFERENCE_NODE) { |
| nextNode = nextNode(nextNode, false); |
| } else { |
| nextNode = nextNode(nextNode, true); |
| } |
| } |
| |
| fForward = true; //REVIST: should direction be set forward before null check? |
| |
| // nothing in the list. return null. |
| if (nextNode == null) return null; |
| |
| // does node pass the filters and whatToShow? |
| accepted = acceptNode(nextNode); |
| if (accepted) { |
| // if so, then the node is the current node. |
| fCurrentNode = nextNode; |
| return fCurrentNode; |
| } else |
| continue accepted_loop; |
| |
| } // while (!accepted) { |
| |
| // no nodes, or no accepted nodes. |
| return null; |
| |
| } |
| |
| /** Return the previous Node in the Iterator. The node is the next node in |
| * _backwards_ depth-first order which also passes the filter, and whatToShow. |
| */ |
| public Node previousNode() { |
| |
| if( fDetach) { |
| throw new DOMException( |
| DOMException.INVALID_STATE_ERR, |
| DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null)); |
| } |
| |
| // if the root is null, or the current node is null, return null. |
| if (fRoot == null || fCurrentNode == null) return null; |
| |
| Node previousNode = fCurrentNode; |
| boolean accepted = false; |
| |
| accepted_loop: |
| while (!accepted) { |
| |
| if (fForward && previousNode != null) { |
| //repeat last node. |
| previousNode = fCurrentNode; |
| } else { |
| // get previous node in backwards depth first order. |
| previousNode = previousNode(previousNode); |
| } |
| |
| // we are going backwards |
| fForward = false; |
| |
| // if the new previous node is null, we're at head or past the root, |
| // so return null. |
| if (previousNode == null) return null; |
| |
| // check if node passes filters and whatToShow. |
| accepted = acceptNode(previousNode); |
| if (accepted) { |
| // if accepted, update the current node, and return it. |
| fCurrentNode = previousNode; |
| return fCurrentNode; |
| } else |
| continue accepted_loop; |
| } |
| // there are no nodes? |
| return null; |
| } |
| |
| /** The node is accepted if it passes the whatToShow and the filter. */ |
| boolean acceptNode(Node node) { |
| |
| if (fNodeFilter == null) { |
| return ( fWhatToShow & (1 << node.getNodeType()-1)) != 0 ; |
| } else { |
| return ((fWhatToShow & (1 << node.getNodeType()-1)) != 0 ) |
| && fNodeFilter.acceptNode(node) == NodeFilter.FILTER_ACCEPT; |
| } |
| } |
| |
| /** Return node, if matches or any parent if matches. */ |
| Node matchNodeOrParent(Node node) { |
| // Additions and removals in the underlying data structure may occur |
| // before any iterations, and in this case the reference_node is null. |
| if (fCurrentNode == null) return null; |
| |
| // check if the removed node is an _ancestor_ of the |
| // reference node |
| for (Node n = fCurrentNode; n != fRoot; n = n.getParentNode()) { |
| if (node == n) return n; |
| } |
| return null; |
| } |
| |
| /** The method nextNode(Node, boolean) returns the next node |
| * from the actual DOM tree. |
| * |
| * The boolean visitChildren determines whether to visit the children. |
| * The result is the nextNode. |
| */ |
| Node nextNode(Node node, boolean visitChildren) { |
| |
| if (node == null) return fRoot; |
| |
| Node result; |
| // only check children if we visit children. |
| if (visitChildren) { |
| //if hasChildren, return 1st child. |
| if (node.hasChildNodes()) { |
| result = node.getFirstChild(); |
| return result; |
| } |
| } |
| |
| if (node == fRoot) { //if Root has no kids |
| return null; |
| } |
| |
| // if hasSibling, return sibling |
| result = node.getNextSibling(); |
| if (result != null) return result; |
| |
| |
| // return parent's 1st sibling. |
| Node parent = node.getParentNode(); |
| while (parent != null && parent != fRoot) { |
| result = parent.getNextSibling(); |
| if (result != null) { |
| return result; |
| } else { |
| parent = parent.getParentNode(); |
| } |
| |
| } // while (parent != null && parent != fRoot) { |
| |
| // end of list, return null |
| return null; |
| } |
| |
| /** The method previousNode(Node) returns the previous node |
| * from the actual DOM tree. |
| */ |
| Node previousNode(Node node) { |
| |
| Node result; |
| |
| // if we're at the root, return null. |
| if (node == fRoot) return null; |
| |
| // get sibling |
| result = node.getPreviousSibling(); |
| if (result == null) { |
| //if 1st sibling, return parent |
| result = node.getParentNode(); |
| return result; |
| } |
| |
| // if sibling has children, keep getting last child of child. |
| if (result.hasChildNodes() |
| && !(!fEntityReferenceExpansion |
| && result != null |
| && result.getNodeType() == Node.ENTITY_REFERENCE_NODE)) |
| |
| { |
| while (result.hasChildNodes()) { |
| result = result.getLastChild(); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** Fix-up the iterator on a remove. Called by DOM or otherwise, |
| * before an actual DOM remove. |
| */ |
| public void removeNode(Node node) { |
| |
| // Implementation note: Fix-up means setting the current node properly |
| // after a remove. |
| |
| if (node == null) return; |
| |
| Node deleted = matchNodeOrParent(node); |
| |
| if (deleted == null) return; |
| |
| if (fForward) { |
| fCurrentNode = previousNode(deleted); |
| } else |
| // if (!fForward) |
| { |
| Node next = nextNode(deleted, false); |
| if (next!=null) { |
| // normal case: there _are_ nodes following this in the iterator. |
| fCurrentNode = next; |
| } else { |
| // the last node in the iterator is to be removed, |
| // so we set the current node to be the previous one. |
| fCurrentNode = previousNode(deleted); |
| fForward = true; |
| } |
| |
| } |
| |
| } |
| |
| public void detach() { |
| fDetach = true; |
| fDocument.removeNodeIterator(this); |
| } |
| |
| } |