001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xerces.dom;
059:
060: import org.w3c.dom.DOMException;
061: import org.w3c.dom.Node;
062: import org.w3c.dom.traversal.NodeFilter;
063: import org.w3c.dom.traversal.NodeIterator;
064:
065: /** DefaultNodeIterator implements a NodeIterator, which iterates a
066: * DOM tree in the expected depth first way.
067: *
068: * <p>The whatToShow and filter functionality is implemented as expected.
069: *
070: * <p>This class also has method removeNode to enable iterator "fix-up"
071: * on DOM remove. It is expected that the DOM implementation call removeNode
072: * right before the actual DOM transformation. If not called by the DOM,
073: * the client could call it before doing the removal.
074: */
075: public class NodeIteratorImpl implements NodeIterator {
076:
077: //
078: // Data
079: //
080:
081: /** The DocumentImpl which created this iterator, so it can be detached. */
082: private DocumentImpl fDocument;
083: /** The root. */
084: private Node fRoot;
085: /** The whatToShow mask. */
086: private int fWhatToShow = NodeFilter.SHOW_ALL;
087: /** The NodeFilter reference. */
088: private NodeFilter fNodeFilter;
089: /** If detach is called, the fDetach flag is true, otherwise flase. */
090: private boolean fDetach = false;
091:
092: //
093: // Iterator state - current node and direction.
094: //
095: // Note: The current node and direction are sufficient to implement
096: // the desired behaviour of the current pointer being _between_
097: // two nodes. The fCurrentNode is actually the last node returned,
098: // and the
099: // direction is whether the pointer is in front or behind this node.
100: // (usually akin to whether the node was returned via nextNode())
101: // (eg fForward = true) or previousNode() (eg fForward = false).
102: // Note also, if removing a Node, the fCurrentNode
103: // can be placed on a Node which would not pass filters.
104:
105: /** The last Node returned. */
106: private Node fCurrentNode;
107:
108: /** The direction of the iterator on the fCurrentNode.
109: * <pre>
110: * nextNode() == fForward = true;
111: * previousNode() == fForward = false;
112: * </pre>
113: */
114: private boolean fForward = true;
115:
116: /** When TRUE, the children of entites references are returned in the iterator. */
117: private boolean fEntityReferenceExpansion;
118:
119: //
120: // Constructor
121: //
122:
123: /** Public constructor */
124: public NodeIteratorImpl(DocumentImpl document, Node root,
125: int whatToShow, NodeFilter nodeFilter,
126: boolean entityReferenceExpansion) {
127: fDocument = document;
128: fRoot = root;
129: fCurrentNode = null;
130: fWhatToShow = whatToShow;
131: fNodeFilter = nodeFilter;
132: fEntityReferenceExpansion = entityReferenceExpansion;
133: }
134:
135: public Node getRoot() {
136: return fRoot;
137: }
138:
139: // Implementation Note: Note that the iterator looks at whatToShow
140: // and filter values at each call, and therefore one _could_ add
141: // setters for these values and alter them while iterating!
142:
143: /** Return the whatToShow value */
144: public int getWhatToShow() {
145: return fWhatToShow;
146: }
147:
148: /** Return the filter */
149: public NodeFilter getFilter() {
150: return fNodeFilter;
151: }
152:
153: /** Return whether children entity references are included in the iterator. */
154: public boolean getExpandEntityReferences() {
155: return fEntityReferenceExpansion;
156: }
157:
158: /** Return the next Node in the Iterator. The node is the next node in
159: * depth-first order which also passes the filter, and whatToShow.
160: * If there is no next node which passes these criteria, then return null.
161: */
162: public Node nextNode() {
163:
164: if (fDetach) {
165: throw new DOMException(DOMException.INVALID_STATE_ERR,
166: "DOM011 Invalid state");
167: }
168:
169: // if root is null there is no next node.
170: if (fRoot == null)
171: return null;
172:
173: Node nextNode = fCurrentNode;
174: boolean accepted = false; // the next node has not been accepted.
175:
176: accepted_loop: while (!accepted) {
177:
178: // if last direction is not forward, repeat node.
179: if (!fForward && nextNode != null) {
180: //System.out.println("nextNode():!fForward:"+fCurrentNode.getNodeName());
181: nextNode = fCurrentNode;
182: } else {
183: // else get the next node via depth-first
184: if (!fEntityReferenceExpansion
185: && nextNode != null
186: && nextNode.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
187: nextNode = nextNode(nextNode, false);
188: } else {
189: nextNode = nextNode(nextNode, true);
190: }
191: }
192:
193: fForward = true; //REVIST: should direction be set forward before null check?
194:
195: // nothing in the list. return null.
196: if (nextNode == null)
197: return null;
198:
199: // does node pass the filters and whatToShow?
200: accepted = acceptNode(nextNode);
201: if (accepted) {
202: // if so, then the node is the current node.
203: fCurrentNode = nextNode;
204: return fCurrentNode;
205: } else
206: continue accepted_loop;
207:
208: } // while (!accepted) {
209:
210: // no nodes, or no accepted nodes.
211: return null;
212:
213: }
214:
215: /** Return the previous Node in the Iterator. The node is the next node in
216: * _backwards_ depth-first order which also passes the filter, and whatToShow.
217: */
218: public Node previousNode() {
219:
220: if (fDetach) {
221: throw new DOMException(DOMException.INVALID_STATE_ERR,
222: "DOM011 Invalid state");
223: }
224:
225: // if the root is null, or the current node is null, return null.
226: if (fRoot == null || fCurrentNode == null)
227: return null;
228:
229: Node previousNode = fCurrentNode;
230: boolean accepted = false;
231:
232: accepted_loop: while (!accepted) {
233:
234: if (fForward && previousNode != null) {
235: //repeat last node.
236: previousNode = fCurrentNode;
237: } else {
238: // get previous node in backwards depth first order.
239: previousNode = previousNode(previousNode);
240: }
241:
242: // we are going backwards
243: fForward = false;
244:
245: // if the new previous node is null, we're at head or past the root,
246: // so return null.
247: if (previousNode == null)
248: return null;
249:
250: // check if node passes filters and whatToShow.
251: accepted = acceptNode(previousNode);
252: if (accepted) {
253: // if accepted, update the current node, and return it.
254: fCurrentNode = previousNode;
255: return fCurrentNode;
256: } else
257: continue accepted_loop;
258: }
259: // there are no nodes?
260: return null;
261: }
262:
263: /** The node is accepted if it passes the whatToShow and the filter. */
264: boolean acceptNode(Node node) {
265:
266: if (fNodeFilter == null) {
267: return (fWhatToShow & (1 << node.getNodeType() - 1)) != 0;
268: } else {
269: return ((fWhatToShow & (1 << node.getNodeType() - 1)) != 0)
270: && fNodeFilter.acceptNode(node) == NodeFilter.FILTER_ACCEPT;
271: }
272: }
273:
274: /** Return node, if matches or any parent if matches. */
275: Node matchNodeOrParent(Node node) {
276: for (Node n = node; n != fRoot; n = n.getParentNode()) {
277: if (node == n)
278: return n;
279: }
280: return null;
281: }
282:
283: /** The method nextNode(Node, boolean) returns the next node
284: * from the actual DOM tree.
285: *
286: * The boolean visitChildren determines whether to visit the children.
287: * The result is the nextNode.
288: */
289: Node nextNode(Node node, boolean visitChildren) {
290:
291: if (node == null)
292: return fRoot;
293:
294: Node result;
295: // only check children if we visit children.
296: if (visitChildren) {
297: //if hasChildren, return 1st child.
298: if (node.hasChildNodes()) {
299: result = node.getFirstChild();
300: return result;
301: }
302: }
303:
304: if (node == fRoot) { //if Root has no kids
305: return null;
306: }
307:
308: // if hasSibling, return sibling
309: result = node.getNextSibling();
310: if (result != null)
311: return result;
312:
313: // return parent's 1st sibling.
314: Node parent = node.getParentNode();
315: while (parent != null && parent != fRoot) {
316: result = parent.getNextSibling();
317: if (result != null) {
318: return result;
319: } else {
320: parent = parent.getParentNode();
321: }
322:
323: } // while (parent != null && parent != fRoot) {
324:
325: // end of list, return null
326: return null;
327: }
328:
329: /** The method previousNode(Node) returns the previous node
330: * from the actual DOM tree.
331: */
332: Node previousNode(Node node) {
333:
334: Node result;
335:
336: // if we're at the root, return null.
337: if (node == fRoot)
338: return null;
339:
340: // get sibling
341: result = node.getPreviousSibling();
342: if (result == null) {
343: //if 1st sibling, return parent
344: result = node.getParentNode();
345: return result;
346: }
347:
348: // if sibling has children, keep getting last child of child.
349: if (result.hasChildNodes()
350: && !(!fEntityReferenceExpansion && result != null && result
351: .getNodeType() == Node.ENTITY_REFERENCE_NODE))
352:
353: {
354: while (result.hasChildNodes()) {
355: result = result.getLastChild();
356: }
357: }
358:
359: return result;
360: }
361:
362: /** Fix-up the iterator on a remove. Called by DOM or otherwise,
363: * before an actual DOM remove.
364: */
365: public void removeNode(Node node) {
366:
367: // Implementation note: Fix-up means setting the current node properly
368: // after a remove.
369:
370: if (node == null)
371: return;
372:
373: Node deleted = matchNodeOrParent(node);
374:
375: if (deleted == null)
376: return;
377:
378: if (fForward) {
379: fCurrentNode = previousNode(deleted);
380: } else
381: // if (!fForward)
382: {
383: Node next = nextNode(deleted, false);
384: if (next != null) {
385: // normal case: there _are_ nodes following this in the iterator.
386: fCurrentNode = next;
387: } else {
388: // the last node in the iterator is to be removed,
389: // so we set the current node to be the previous one.
390: fCurrentNode = previousNode(deleted);
391: fForward = true;
392: }
393:
394: }
395:
396: }
397:
398: public void detach() {
399: fDetach = true;
400: fDocument.removeNodeIterator(this);
401: }
402:
403: }
|