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