| /******************************************************************************* |
| * Copyright (c) 2001, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.core.internal.document; |
| |
| |
| |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.traversal.NodeFilter; |
| import org.w3c.dom.traversal.NodeIterator; |
| |
| /** |
| * NodeIteratorImpl class |
| */ |
| public class NodeIteratorImpl implements NodeIterator { |
| private NodeFilter filter = null; |
| private Node nextNode = null; |
| |
| private Node rootNode = null; |
| private int whatToShow = NodeFilter.SHOW_ALL; |
| |
| /** |
| * NodeIteratorImpl constructor |
| * |
| * @param rootNode |
| * org.w3c.dom.Node |
| */ |
| NodeIteratorImpl(Node rootNode, int whatToShow, NodeFilter filter) { |
| this.rootNode = rootNode; |
| this.nextNode = rootNode; |
| this.whatToShow = whatToShow; |
| this.filter = filter; |
| } |
| |
| /** |
| */ |
| private final boolean acceptNode(Node node) { |
| if (this.whatToShow != NodeFilter.SHOW_ALL) { |
| if (node == null) |
| return false; |
| short nodeType = node.getNodeType(); |
| switch (this.whatToShow) { |
| case NodeFilter.SHOW_ELEMENT : |
| if (nodeType != Node.ELEMENT_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_ATTRIBUTE : |
| if (nodeType != Node.ATTRIBUTE_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_TEXT : |
| if (nodeType != Node.TEXT_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_CDATA_SECTION : |
| if (nodeType != Node.CDATA_SECTION_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_ENTITY_REFERENCE : |
| if (nodeType != Node.ENTITY_REFERENCE_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_ENTITY : |
| if (nodeType != Node.ENTITY_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_PROCESSING_INSTRUCTION : |
| if (nodeType != Node.PROCESSING_INSTRUCTION_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_COMMENT : |
| if (nodeType != Node.COMMENT_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_DOCUMENT : |
| if (nodeType != Node.DOCUMENT_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_DOCUMENT_TYPE : |
| if (nodeType != Node.DOCUMENT_TYPE_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_DOCUMENT_FRAGMENT : |
| if (nodeType != Node.DOCUMENT_FRAGMENT_NODE) |
| return false; |
| break; |
| case NodeFilter.SHOW_NOTATION : |
| if (nodeType != Node.NOTATION_NODE) |
| return false; |
| break; |
| default : |
| return false; |
| } |
| } |
| if (this.filter != null) { |
| return (this.filter.acceptNode(node) == NodeFilter.FILTER_ACCEPT); |
| } |
| return true; |
| } |
| |
| /** |
| * Detaches the <code>NodeIterator</code> from the set which it iterated |
| * over, releasing any computational resources and placing the iterator in |
| * the INVALID state. After <code>detach</code> has been invoked, calls |
| * to <code>nextNode</code> or <code>previousNode</code> will raise |
| * the exception INVALID_STATE_ERR. |
| */ |
| public void detach() { |
| this.rootNode = null; |
| this.nextNode = null; |
| this.filter = null; |
| } |
| |
| /** |
| * The value of this flag determines whether the children of entity |
| * reference nodes are visible to the iterator. If false, they and their |
| * descendants will be rejected. Note that this rejection takes precedence |
| * over <code>whatToShow</code> and the filter. Also note that this is |
| * currently the only situation where <code>NodeIterators</code> may |
| * reject a complete subtree rather than skipping individual nodes. <br> |
| * <br> |
| * To produce a view of the document that has entity references expanded |
| * and does not expose the entity reference node itself, use the |
| * <code>whatToShow</code> flags to hide the entity reference node and |
| * set <code>expandEntityReferences</code> to true when creating the |
| * iterator. To produce a view of the document that has entity reference |
| * nodes but no entity expansion, use the <code>whatToShow</code> flags |
| * to show the entity reference node and set |
| * <code>expandEntityReferences</code> to false. |
| */ |
| public boolean getExpandEntityReferences() { |
| // not supported |
| return false; |
| } |
| |
| /** |
| * The <code>NodeFilter</code> used to screen nodes. |
| */ |
| public NodeFilter getFilter() { |
| return this.filter; |
| } |
| |
| /** |
| */ |
| private final Node getNextNode() { |
| if (this.nextNode == null) |
| return null; |
| Node oldNext = this.nextNode; |
| Node child = this.nextNode.getFirstChild(); |
| if (child != null) { |
| this.nextNode = child; |
| return oldNext; |
| } |
| for (Node node = this.nextNode; node != null && node != this.rootNode; node = node.getParentNode()) { |
| Node next = node.getNextSibling(); |
| if (next != null) { |
| this.nextNode = next; |
| return oldNext; |
| } |
| } |
| this.nextNode = null; |
| return oldNext; |
| } |
| |
| /** |
| */ |
| private final Node getPreviousNode() { |
| if (this.nextNode == this.rootNode) |
| return null; |
| Node prev = null; |
| if (this.nextNode == null) { |
| prev = this.rootNode; // never null |
| } else { |
| prev = this.nextNode.getPreviousSibling(); |
| if (prev == null) { |
| this.nextNode = this.nextNode.getParentNode(); |
| return this.nextNode; |
| } |
| } |
| Node last = prev.getLastChild(); |
| while (last != null) { |
| prev = last; |
| last = prev.getLastChild(); |
| } |
| this.nextNode = prev; |
| return this.nextNode; |
| } |
| |
| /** |
| * The root node of the <code>NodeIterator</code>, as specified when it |
| * was created. |
| */ |
| public Node getRoot() { |
| return this.rootNode; |
| } |
| |
| /** |
| * This attribute determines which node types are presented via the |
| * iterator. The available set of constants is defined in the |
| * <code>NodeFilter</code> interface. Nodes not accepted by |
| * <code>whatToShow</code> will be skipped, but their children may still |
| * be considered. Note that this skip takes precedence over the filter, if |
| * any. |
| */ |
| public int getWhatToShow() { |
| return this.whatToShow; |
| } |
| |
| /** |
| * Returns the next node in the set and advances the position of the |
| * iterator in the set. After a <code>NodeIterator</code> is created, |
| * the first call to <code>nextNode()</code> returns the first node in |
| * the set. |
| * |
| * @return The next <code>Node</code> in the set being iterated over, or |
| * <code>null</code> if there are no more members in that set. |
| * @exception DOMException |
| * INVALID_STATE_ERR: Raised if this method is called after |
| * the <code>detach</code> method was invoked. |
| */ |
| public Node nextNode() throws DOMException { |
| for (Node node = getNextNode(); node != null; node = getNextNode()) { |
| if (acceptNode(node)) |
| return node; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the previous node in the set and moves the position of the |
| * <code>NodeIterator</code> backwards in the set. |
| * |
| * @return The previous <code>Node</code> in the set being iterated |
| * over, or <code>null</code> if there are no more members in |
| * that set. |
| * @exception DOMException |
| * INVALID_STATE_ERR: Raised if this method is called after |
| * the <code>detach</code> method was invoked. |
| */ |
| public Node previousNode() throws DOMException { |
| for (Node node = getPreviousNode(); node != null; node = getPreviousNode()) { |
| if (acceptNode(node)) |
| return node; |
| } |
| return null; |
| } |
| } |