001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.dom;
019:
020: import org.w3c.dom.DOMException;
021: import org.w3c.dom.Node;
022: import org.w3c.dom.traversal.NodeFilter;
023: import org.w3c.dom.traversal.NodeIterator;
024:
025: /** DefaultNodeIterator implements a NodeIterator, which iterates a
026: * DOM tree in the expected depth first way.
027: *
028: * <p>The whatToShow and filter functionality is implemented as expected.
029: *
030: * <p>This class also has method removeNode to enable iterator "fix-up"
031: * on DOM remove. It is expected that the DOM implementation call removeNode
032: * right before the actual DOM transformation. If not called by the DOM,
033: * the client could call it before doing the removal.
034: *
035: * @xerces.internal
036: *
037: * @version $Id: NodeIteratorImpl.java 447266 2006-09-18 05:57:49Z mrglavas $
038: */
039: public class NodeIteratorImpl implements NodeIterator {
040:
041: //
042: // Data
043: //
044:
045: /** The DocumentImpl which created this iterator, so it can be detached. */
046: private DocumentImpl fDocument;
047: /** The root. */
048: private Node fRoot;
049: /** The whatToShow mask. */
050: private int fWhatToShow = NodeFilter.SHOW_ALL;
051: /** The NodeFilter reference. */
052: private NodeFilter fNodeFilter;
053: /** If detach is called, the fDetach flag is true, otherwise flase. */
054: private boolean fDetach = false;
055:
056: //
057: // Iterator state - current node and direction.
058: //
059: // Note: The current node and direction are sufficient to implement
060: // the desired behaviour of the current pointer being _between_
061: // two nodes. The fCurrentNode is actually the last node returned,
062: // and the
063: // direction is whether the pointer is in front or behind this node.
064: // (usually akin to whether the node was returned via nextNode())
065: // (eg fForward = true) or previousNode() (eg fForward = false).
066: // Note also, if removing a Node, the fCurrentNode
067: // can be placed on a Node which would not pass filters.
068:
069: /** The last Node returned. */
070: private Node fCurrentNode;
071:
072: /** The direction of the iterator on the fCurrentNode.
073: * <pre>
074: * nextNode() == fForward = true;
075: * previousNode() == fForward = false;
076: * </pre>
077: */
078: private boolean fForward = true;
079:
080: /** When TRUE, the children of entites references are returned in the iterator. */
081: private boolean fEntityReferenceExpansion;
082:
083: //
084: // Constructor
085: //
086:
087: /** Public constructor */
088: public NodeIteratorImpl(DocumentImpl document, Node root,
089: int whatToShow, NodeFilter nodeFilter,
090: boolean entityReferenceExpansion) {
091: fDocument = document;
092: fRoot = root;
093: fCurrentNode = null;
094: fWhatToShow = whatToShow;
095: fNodeFilter = nodeFilter;
096: fEntityReferenceExpansion = entityReferenceExpansion;
097: }
098:
099: public Node getRoot() {
100: return fRoot;
101: }
102:
103: // Implementation Note: Note that the iterator looks at whatToShow
104: // and filter values at each call, and therefore one _could_ add
105: // setters for these values and alter them while iterating!
106:
107: /** Return the whatToShow value */
108: public int getWhatToShow() {
109: return fWhatToShow;
110: }
111:
112: /** Return the filter */
113: public NodeFilter getFilter() {
114: return fNodeFilter;
115: }
116:
117: /** Return whether children entity references are included in the iterator. */
118: public boolean getExpandEntityReferences() {
119: return fEntityReferenceExpansion;
120: }
121:
122: /** Return the next Node in the Iterator. The node is the next node in
123: * depth-first order which also passes the filter, and whatToShow.
124: * If there is no next node which passes these criteria, then return null.
125: */
126: public Node nextNode() {
127:
128: if (fDetach) {
129: throw new DOMException(DOMException.INVALID_STATE_ERR,
130: DOMMessageFormatter.formatMessage(
131: DOMMessageFormatter.DOM_DOMAIN,
132: "INVALID_STATE_ERR", null));
133: }
134:
135: // if root is null there is no next node.
136: if (fRoot == null)
137: return null;
138:
139: Node nextNode = fCurrentNode;
140: boolean accepted = false; // the next node has not been accepted.
141:
142: accepted_loop: while (!accepted) {
143:
144: // if last direction is not forward, repeat node.
145: if (!fForward && nextNode != null) {
146: //System.out.println("nextNode():!fForward:"+fCurrentNode.getNodeName());
147: nextNode = fCurrentNode;
148: } else {
149: // else get the next node via depth-first
150: if (!fEntityReferenceExpansion
151: && nextNode != null
152: && nextNode.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
153: nextNode = nextNode(nextNode, false);
154: } else {
155: nextNode = nextNode(nextNode, true);
156: }
157: }
158:
159: fForward = true; //REVIST: should direction be set forward before null check?
160:
161: // nothing in the list. return null.
162: if (nextNode == null)
163: return null;
164:
165: // does node pass the filters and whatToShow?
166: accepted = acceptNode(nextNode);
167: if (accepted) {
168: // if so, then the node is the current node.
169: fCurrentNode = nextNode;
170: return fCurrentNode;
171: } else
172: continue accepted_loop;
173:
174: } // while (!accepted) {
175:
176: // no nodes, or no accepted nodes.
177: return null;
178:
179: }
180:
181: /** Return the previous Node in the Iterator. The node is the next node in
182: * _backwards_ depth-first order which also passes the filter, and whatToShow.
183: */
184: public Node previousNode() {
185:
186: if (fDetach) {
187: throw new DOMException(DOMException.INVALID_STATE_ERR,
188: DOMMessageFormatter.formatMessage(
189: DOMMessageFormatter.DOM_DOMAIN,
190: "INVALID_STATE_ERR", null));
191: }
192:
193: // if the root is null, or the current node is null, return null.
194: if (fRoot == null || fCurrentNode == null)
195: return null;
196:
197: Node previousNode = fCurrentNode;
198: boolean accepted = false;
199:
200: accepted_loop: while (!accepted) {
201:
202: if (fForward && previousNode != null) {
203: //repeat last node.
204: previousNode = fCurrentNode;
205: } else {
206: // get previous node in backwards depth first order.
207: previousNode = previousNode(previousNode);
208: }
209:
210: // we are going backwards
211: fForward = false;
212:
213: // if the new previous node is null, we're at head or past the root,
214: // so return null.
215: if (previousNode == null)
216: return null;
217:
218: // check if node passes filters and whatToShow.
219: accepted = acceptNode(previousNode);
220: if (accepted) {
221: // if accepted, update the current node, and return it.
222: fCurrentNode = previousNode;
223: return fCurrentNode;
224: } else
225: continue accepted_loop;
226: }
227: // there are no nodes?
228: return null;
229: }
230:
231: /** The node is accepted if it passes the whatToShow and the filter. */
232: boolean acceptNode(Node node) {
233:
234: if (fNodeFilter == null) {
235: return (fWhatToShow & (1 << node.getNodeType() - 1)) != 0;
236: } else {
237: return ((fWhatToShow & (1 << node.getNodeType() - 1)) != 0)
238: && fNodeFilter.acceptNode(node) == NodeFilter.FILTER_ACCEPT;
239: }
240: }
241:
242: /** Return node, if matches or any parent if matches. */
243: Node matchNodeOrParent(Node node) {
244: // Additions and removals in the underlying data structure may occur
245: // before any iterations, and in this case the reference_node is null.
246: if (fCurrentNode == null)
247: return null;
248:
249: // check if the removed node is an _ancestor_ of the
250: // reference node
251: for (Node n = fCurrentNode; n != fRoot; n = n.getParentNode()) {
252: if (node == n)
253: return n;
254: }
255: return null;
256: }
257:
258: /** The method nextNode(Node, boolean) returns the next node
259: * from the actual DOM tree.
260: *
261: * The boolean visitChildren determines whether to visit the children.
262: * The result is the nextNode.
263: */
264: Node nextNode(Node node, boolean visitChildren) {
265:
266: if (node == null)
267: return fRoot;
268:
269: Node result;
270: // only check children if we visit children.
271: if (visitChildren) {
272: //if hasChildren, return 1st child.
273: if (node.hasChildNodes()) {
274: result = node.getFirstChild();
275: return result;
276: }
277: }
278:
279: if (node == fRoot) { //if Root has no kids
280: return null;
281: }
282:
283: // if hasSibling, return sibling
284: result = node.getNextSibling();
285: if (result != null)
286: return result;
287:
288: // return parent's 1st sibling.
289: Node parent = node.getParentNode();
290: while (parent != null && parent != fRoot) {
291: result = parent.getNextSibling();
292: if (result != null) {
293: return result;
294: } else {
295: parent = parent.getParentNode();
296: }
297:
298: } // while (parent != null && parent != fRoot) {
299:
300: // end of list, return null
301: return null;
302: }
303:
304: /** The method previousNode(Node) returns the previous node
305: * from the actual DOM tree.
306: */
307: Node previousNode(Node node) {
308:
309: Node result;
310:
311: // if we're at the root, return null.
312: if (node == fRoot)
313: return null;
314:
315: // get sibling
316: result = node.getPreviousSibling();
317: if (result == null) {
318: //if 1st sibling, return parent
319: result = node.getParentNode();
320: return result;
321: }
322:
323: // if sibling has children, keep getting last child of child.
324: if (result.hasChildNodes()
325: && !(!fEntityReferenceExpansion && result != null && result
326: .getNodeType() == Node.ENTITY_REFERENCE_NODE))
327:
328: {
329: while (result.hasChildNodes()) {
330: result = result.getLastChild();
331: }
332: }
333:
334: return result;
335: }
336:
337: /** Fix-up the iterator on a remove. Called by DOM or otherwise,
338: * before an actual DOM remove.
339: */
340: public void removeNode(Node node) {
341:
342: // Implementation note: Fix-up means setting the current node properly
343: // after a remove.
344:
345: if (node == null)
346: return;
347:
348: Node deleted = matchNodeOrParent(node);
349:
350: if (deleted == null)
351: return;
352:
353: if (fForward) {
354: fCurrentNode = previousNode(deleted);
355: } else
356: // if (!fForward)
357: {
358: Node next = nextNode(deleted, false);
359: if (next != null) {
360: // normal case: there _are_ nodes following this in the iterator.
361: fCurrentNode = next;
362: } else {
363: // the last node in the iterator is to be removed,
364: // so we set the current node to be the previous one.
365: fCurrentNode = previousNode(deleted);
366: fForward = true;
367: }
368:
369: }
370:
371: }
372:
373: public void detach() {
374: fDetach = true;
375: fDocument.removeNodeIterator(this);
376: }
377:
378: }
|