001: /*
002: * Copyright 2002-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: NodeSequence.java,v 1.14 2005/01/23 01:02:10 mcnamara Exp $
018: */
019: package org.apache.xpath.axes;
020:
021: import java.util.Vector;
022:
023: import org.apache.xml.dtm.DTM;
024: import org.apache.xml.dtm.DTMFilter;
025: import org.apache.xml.dtm.DTMIterator;
026: import org.apache.xml.dtm.DTMManager;
027: import org.apache.xml.utils.NodeVector;
028: import org.apache.xpath.NodeSetDTM;
029: import org.apache.xpath.XPathContext;
030: import org.apache.xpath.objects.XObject;
031:
032: /**
033: * This class is the dynamic wrapper for a Xalan DTMIterator instance, and
034: * provides random access capabilities.
035: */
036: public class NodeSequence extends XObject implements DTMIterator,
037: Cloneable, PathComponent {
038: static final long serialVersionUID = 3866261934726581044L;
039: /** The index of the last node in the iteration. */
040: protected int m_last = -1;
041:
042: /**
043: * The index of the next node to be fetched. Useful if this
044: * is a cached iterator, and is being used as random access
045: * NodeList.
046: */
047: protected int m_next = 0;
048:
049: /**
050: * If this iterator needs to cache nodes that are fetched, they
051: * are stored in the Vector in the generic object.
052: */
053: protected NodeVector getVector() {
054: return (NodeVector) m_obj;
055: }
056:
057: /**
058: * Set the vector where nodes will be stored.
059: */
060: protected void SetVector(NodeVector v) {
061: m_obj = v;
062: }
063:
064: /**
065: * If this iterator needs to cache nodes that are fetched, they
066: * are stored here.
067: */
068: public boolean hasCache() {
069: return (m_obj != null);
070: }
071:
072: /**
073: * The functional iterator that fetches nodes.
074: */
075: protected DTMIterator m_iter;
076:
077: /**
078: * Set the functional iterator that fetches nodes.
079: * @param iter The iterator that is to be contained.
080: */
081: public final void setIter(DTMIterator iter) {
082: m_iter = iter;
083: }
084:
085: /**
086: * Get the functional iterator that fetches nodes.
087: * @return The contained iterator.
088: */
089: public final DTMIterator getContainedIter() {
090: return m_iter;
091: }
092:
093: /**
094: * The DTMManager to use if we're using a NodeVector only.
095: * We may well want to do away with this, and store it in the NodeVector.
096: */
097: protected DTMManager m_dtmMgr;
098:
099: // ==== Constructors ====
100:
101: /**
102: * Create a new NodeSequence from a (already cloned) iterator.
103: *
104: * @param iter Cloned (not static) DTMIterator.
105: * @param context The initial context node.
106: * @param xctxt The execution context.
107: * @param shouldCacheNodes True if this sequence can random access.
108: */
109: public NodeSequence(DTMIterator iter, int context,
110: XPathContext xctxt, boolean shouldCacheNodes) {
111: setIter(iter);
112: setRoot(context, xctxt);
113: setShouldCacheNodes(shouldCacheNodes);
114: }
115:
116: /**
117: * Create a new NodeSequence from a (already cloned) iterator.
118: *
119: * @param nodeVector
120: */
121: public NodeSequence(Object nodeVector) {
122: super (nodeVector);
123: if (null != nodeVector) {
124: assertion(nodeVector instanceof NodeVector,
125: "Must have a NodeVector as the object for NodeSequence!");
126: if (nodeVector instanceof DTMIterator) {
127: setIter((DTMIterator) nodeVector);
128: m_last = ((DTMIterator) nodeVector).getLength();
129: }
130:
131: }
132: }
133:
134: /**
135: * Construct an empty XNodeSet object. This is used to create a mutable
136: * nodeset to which random nodes may be added.
137: */
138: public NodeSequence(DTMManager dtmMgr) {
139: super (new NodeVector());
140: m_last = 0;
141: m_dtmMgr = dtmMgr;
142: }
143:
144: /**
145: * Create a new NodeSequence in an invalid (null) state.
146: */
147: public NodeSequence() {
148: }
149:
150: /**
151: * @see DTMIterator#getDTM(int)
152: */
153: public DTM getDTM(int nodeHandle) {
154: DTMManager mgr = getDTMManager();
155: if (null != mgr)
156: return getDTMManager().getDTM(nodeHandle);
157: else {
158: assertion(false,
159: "Can not get a DTM Unless a DTMManager has been set!");
160: return null;
161: }
162: }
163:
164: /**
165: * @see DTMIterator#getDTMManager()
166: */
167: public DTMManager getDTMManager() {
168: return m_dtmMgr;
169: }
170:
171: /**
172: * @see DTMIterator#getRoot()
173: */
174: public int getRoot() {
175: if (null != m_iter)
176: return m_iter.getRoot();
177: else {
178: // NodeSetDTM will call this, and so it's not a good thing to throw
179: // an assertion here.
180: // assertion(false, "Can not get the root from a non-iterated NodeSequence!");
181: return DTM.NULL;
182: }
183: }
184:
185: /**
186: * @see DTMIterator#setRoot(int, Object)
187: */
188: public void setRoot(int nodeHandle, Object environment) {
189: if (null != m_iter) {
190: XPathContext xctxt = (XPathContext) environment;
191: m_dtmMgr = xctxt.getDTMManager();
192: m_iter.setRoot(nodeHandle, environment);
193: if (!m_iter.isDocOrdered()) {
194: if (!hasCache())
195: setShouldCacheNodes(true);
196: runTo(-1);
197: m_next = 0;
198: }
199: } else
200: assertion(false,
201: "Can not setRoot on a non-iterated NodeSequence!");
202: }
203:
204: /**
205: * @see DTMIterator#reset()
206: */
207: public void reset() {
208: m_next = 0;
209: // not resetting the iterator on purpose!!!
210: }
211:
212: /**
213: * @see DTMIterator#getWhatToShow()
214: */
215: public int getWhatToShow() {
216: return hasCache() ? (DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE)
217: : m_iter.getWhatToShow();
218: }
219:
220: /**
221: * @see DTMIterator#getExpandEntityReferences()
222: */
223: public boolean getExpandEntityReferences() {
224: if (null != m_iter)
225: return m_iter.getExpandEntityReferences();
226: else
227: return true;
228: }
229:
230: /**
231: * @see DTMIterator#nextNode()
232: */
233: public int nextNode() {
234: // If the cache is on, and the node has already been found, then
235: // just return from the list.
236: NodeVector vec = getVector();
237: if (null != vec) {
238: if (m_next < vec.size()) {
239: int next = vec.elementAt(m_next);
240: m_next++;
241: return next;
242: } else if ((-1 != m_last) || (null == m_iter)) {
243: m_next++;
244: return DTM.NULL;
245: }
246: }
247:
248: if (null == m_iter)
249: return DTM.NULL;
250:
251: int next = m_iter.nextNode();
252: if (DTM.NULL != next) {
253: if (hasCache()) {
254: if (m_iter.isDocOrdered()) {
255: getVector().addElement(next);
256: m_next++;
257: } else {
258: int insertIndex = addNodeInDocOrder(next);
259: if (insertIndex >= 0)
260: m_next++;
261: }
262: } else
263: m_next++;
264: } else {
265: m_last = m_next;
266: m_next++;
267: }
268:
269: return next;
270: }
271:
272: /**
273: * @see DTMIterator#previousNode()
274: */
275: public int previousNode() {
276: if (hasCache()) {
277: if (m_next <= 0)
278: return DTM.NULL;
279: else {
280: m_next--;
281: return item(m_next);
282: }
283: } else {
284: int n = m_iter.previousNode();
285: m_next = m_iter.getCurrentPos();
286: return m_next;
287: }
288: }
289:
290: /**
291: * @see DTMIterator#detach()
292: */
293: public void detach() {
294: if (null != m_iter)
295: m_iter.detach();
296: super .detach();
297: }
298:
299: /**
300: * Calling this with a value of false will cause the nodeset
301: * to be cached.
302: * @see DTMIterator#allowDetachToRelease(boolean)
303: */
304: public void allowDetachToRelease(boolean allowRelease) {
305: if ((false == allowRelease) && !hasCache()) {
306: setShouldCacheNodes(true);
307: }
308:
309: if (null != m_iter)
310: m_iter.allowDetachToRelease(allowRelease);
311: super .allowDetachToRelease(allowRelease);
312: }
313:
314: /**
315: * @see DTMIterator#getCurrentNode()
316: */
317: public int getCurrentNode() {
318: if (hasCache()) {
319: int currentIndex = m_next - 1;
320: NodeVector vec = getVector();
321: if ((currentIndex >= 0) && (currentIndex < vec.size()))
322: return vec.elementAt(currentIndex);
323: else
324: return DTM.NULL;
325: }
326:
327: if (null != m_iter) {
328: return m_iter.getCurrentNode();
329: } else
330: return DTM.NULL;
331: }
332:
333: /**
334: * @see DTMIterator#isFresh()
335: */
336: public boolean isFresh() {
337: return (0 == m_next);
338: }
339:
340: /**
341: * @see DTMIterator#setShouldCacheNodes(boolean)
342: */
343: public void setShouldCacheNodes(boolean b) {
344: if (b) {
345: if (!hasCache()) {
346: SetVector(new NodeVector());
347: }
348: // else
349: // getVector().RemoveAllNoClear(); // Is this good?
350: } else
351: SetVector(null);
352: }
353:
354: /**
355: * @see DTMIterator#isMutable()
356: */
357: public boolean isMutable() {
358: return hasCache(); // though may be surprising if it also has an iterator!
359: }
360:
361: /**
362: * @see DTMIterator#getCurrentPos()
363: */
364: public int getCurrentPos() {
365: return m_next;
366: }
367:
368: /**
369: * @see DTMIterator#runTo(int)
370: */
371: public void runTo(int index) {
372: int n;
373:
374: if (-1 == index) {
375: int pos = m_next;
376: while (DTM.NULL != (n = nextNode()))
377: ;
378: m_next = pos;
379: } else if (m_next == index) {
380: return;
381: } else if (hasCache() && m_next < getVector().size()) {
382: m_next = index;
383: } else if ((null == getVector()) && (index < m_next)) {
384: while ((m_next >= index)
385: && DTM.NULL != (n = previousNode()))
386: ;
387: } else {
388: while ((m_next < index) && DTM.NULL != (n = nextNode()))
389: ;
390: }
391:
392: }
393:
394: /**
395: * @see DTMIterator#setCurrentPos(int)
396: */
397: public void setCurrentPos(int i) {
398: runTo(i);
399: }
400:
401: /**
402: * @see DTMIterator#item(int)
403: */
404: public int item(int index) {
405: setCurrentPos(index);
406: int n = nextNode();
407: m_next = index;
408: return n;
409: }
410:
411: /**
412: * @see DTMIterator#setItem(int, int)
413: */
414: public void setItem(int node, int index) {
415: NodeVector vec = getVector();
416: if (null != vec) {
417: vec.setElementAt(node, index);
418: m_last = vec.size();
419: } else
420: m_iter.setItem(node, index);
421: }
422:
423: /**
424: * @see DTMIterator#getLength()
425: */
426: public int getLength() {
427: if (hasCache()) {
428: // If this NodeSequence wraps a mutable nodeset, then
429: // m_last will not reflect the size of the nodeset if
430: // it has been mutated...
431: if (m_iter instanceof NodeSetDTM) {
432: return m_iter.getLength();
433: }
434:
435: if (-1 == m_last) {
436: int pos = m_next;
437: runTo(-1);
438: m_next = pos;
439: }
440: return m_last;
441: } else {
442: return (-1 == m_last) ? (m_last = m_iter.getLength())
443: : m_last;
444: }
445: }
446:
447: /**
448: * Note: Not a deep clone.
449: * @see DTMIterator#cloneWithReset()
450: */
451: public DTMIterator cloneWithReset()
452: throws CloneNotSupportedException {
453: NodeSequence seq = (NodeSequence) super .clone();
454: seq.m_next = 0;
455: return seq;
456: }
457:
458: /**
459: * Get a clone of this iterator, but don't reset the iteration in the
460: * process, so that it may be used from the current position.
461: * Note: Not a deep clone.
462: *
463: * @return A clone of this object.
464: *
465: * @throws CloneNotSupportedException
466: */
467: public Object clone() throws CloneNotSupportedException {
468: NodeSequence clone = (NodeSequence) super .clone();
469: if (null != m_iter)
470: clone.m_iter = (DTMIterator) m_iter.clone();
471: return clone;
472: }
473:
474: /**
475: * @see DTMIterator#isDocOrdered()
476: */
477: public boolean isDocOrdered() {
478: if (null != m_iter)
479: return m_iter.isDocOrdered();
480: else
481: return true; // can't be sure?
482: }
483:
484: /**
485: * @see DTMIterator#getAxis()
486: */
487: public int getAxis() {
488: if (null != m_iter)
489: return m_iter.getAxis();
490: else {
491: assertion(false,
492: "Can not getAxis from a non-iterated node sequence!");
493: return 0;
494: }
495: }
496:
497: /**
498: * @see PathComponent#getAnalysisBits()
499: */
500: public int getAnalysisBits() {
501: if ((null != m_iter) && (m_iter instanceof PathComponent))
502: return ((PathComponent) m_iter).getAnalysisBits();
503: else
504: return 0;
505: }
506:
507: /**
508: * @see org.apache.xpath.Expression#fixupVariables(Vector, int)
509: */
510: public void fixupVariables(Vector vars, int globalsSize) {
511: super .fixupVariables(vars, globalsSize);
512: }
513:
514: /**
515: * Add the node into a vector of nodes where it should occur in
516: * document order.
517: * @param node The node to be added.
518: * @return insertIndex.
519: * @throws RuntimeException thrown if this NodeSetDTM is not of
520: * a mutable type.
521: */
522: protected int addNodeInDocOrder(int node) {
523: assertion(hasCache(),
524: "addNodeInDocOrder must be done on a mutable sequence!");
525:
526: int insertIndex = -1;
527:
528: NodeVector vec = getVector();
529:
530: // This needs to do a binary search, but a binary search
531: // is somewhat tough because the sequence test involves
532: // two nodes.
533: int size = vec.size(), i;
534:
535: for (i = size - 1; i >= 0; i--) {
536: int child = vec.elementAt(i);
537:
538: if (child == node) {
539: i = -2; // Duplicate, suppress insert
540:
541: break;
542: }
543:
544: DTM dtm = m_dtmMgr.getDTM(node);
545: if (!dtm.isNodeAfter(node, child)) {
546: break;
547: }
548: }
549:
550: if (i != -2) {
551: insertIndex = i + 1;
552:
553: vec.insertElementAt(node, insertIndex);
554: }
555:
556: // checkDups();
557: return insertIndex;
558: } // end addNodeInDocOrder(Vector v, Object obj)
559: }
|