View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2006 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: KeyIndex.java,v 1.6 2006/06/19 19:49:02 spericas Exp $
22   */
23  
24  package com.sun.org.apache.xalan.internal.xsltc.dom;
25  
26  import java.util.StringTokenizer;
27  
28  import com.sun.org.apache.xalan.internal.xsltc.DOM;
29  import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
30  import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
31  import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
32  import com.sun.org.apache.xalan.internal.xsltc.util.IntegerArray;
33  
34  import com.sun.org.apache.xml.internal.dtm.Axis;
35  import com.sun.org.apache.xml.internal.dtm.DTM;
36  import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
37  import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIteratorBase;
38  
39  /**
40   * Stores mappings of key values or IDs to DTM nodes.
41   * <em>Use of an instance of this class as a {@link DTMAxisIterator} is
42   * <b>deprecated.</b></em>
43   * @author Morten Jorgensen
44   * @author Santiago Pericas-Geertsen
45   */
46  public class KeyIndex extends DTMAxisIteratorBase {
47  
48      /**
49       * A mapping between values and nodesets for the current document.  Used
50       * only while building keys.
51       */
52      private Hashtable _index;
53  
54      /**
55       * The document node currently being processed.  Used only while building
56       * keys.
57       */
58      private int _currentDocumentNode = DTM.NULL;
59  
60      /**
61       * A mapping from a document node to the mapping between values and nodesets
62       */
63      private Hashtable _rootToIndexMap = new Hashtable();
64  
65      /**
66       * The node set associated to the current value passed
67       * to lookupKey();
68       */
69      private IntegerArray _nodes = null;
70  
71      /**
72       * The XSLTC DOM object if this KeyIndex is being used to implement the
73       * id() function.
74       */
75      private DOM        _dom;
76  
77      private DOMEnhancedForDTM    _enhancedDOM;
78  
79      /**
80       * Store position after call to setMark()
81       */
82      private int _markedPosition = 0;
83  
84      public KeyIndex(int dummy) {
85      }
86  
87      public void setRestartable(boolean flag) {
88      }
89  
90      /**
91       * Adds a node to the node list for a given value. Nodes will
92       * always be added in document order.
93       */
94      public void add(Object value, int node, int rootNode) {
95          if (_currentDocumentNode != rootNode) {
96              _currentDocumentNode = rootNode;
97              _index = new Hashtable();
98              _rootToIndexMap.put(new Integer(rootNode), _index);
99          }
100 
101         IntegerArray nodes = (IntegerArray) _index.get(value);
102 
103         if (nodes == null) {
104              nodes = new IntegerArray();
105             _index.put(value, nodes);
106             nodes.add(node);
107 
108         // Because nodes are added in document order,
109         // duplicates can be eliminated easily at this stage.
110         } else if (node != nodes.at(nodes.cardinality() - 1)) {
111             nodes.add(node);
112         }
113     }
114 
115     /**
116      * Merge the current value's nodeset set by lookupKey() with _nodes.
117      * @deprecated
118      */
119     public void merge(KeyIndex other) {
120         if (other == null) return;
121 
122         if (other._nodes != null) {
123             if (_nodes == null) {
124                 _nodes = (IntegerArray)other._nodes.clone();
125             }
126             else {
127                 _nodes.merge(other._nodes);
128             }
129         }
130     }
131 
132     /**
133      * This method must be called by the code generated by the id() function
134      * prior to returning the node iterator. The lookup code for key() and
135      * id() differ in the way the lookup value can be whitespace separated
136      * list of tokens for the id() function, but a single string for the
137      * key() function.
138      * @deprecated
139      */
140     public void lookupId(Object value) {
141         // Clear _nodes array
142         _nodes = null;
143 
144         final StringTokenizer values = new StringTokenizer((String) value,
145                                                            " \n\t");
146         while (values.hasMoreElements()) {
147             final String token = (String) values.nextElement();
148             IntegerArray nodes = (IntegerArray) _index.get(token);
149 
150             if (nodes == null && _enhancedDOM != null
151                 && _enhancedDOM.hasDOMSource()) {
152                 nodes = getDOMNodeById(token);
153             }
154 
155             if (nodes == null) continue;
156 
157             if (_nodes == null) {
158                  nodes = (IntegerArray)nodes.clone();
159                 _nodes = nodes;
160             }
161             else {
162                 _nodes.merge(nodes);
163             }
164         }
165     }
166 
167     /**
168      * Return an IntegerArray for the DOM Node which has the given id.
169      *
170      * @param id The id
171      * @return A IntegerArray representing the Node whose id is the given value.
172      */
173     public IntegerArray getDOMNodeById(String id) {
174         IntegerArray nodes = null;
175 
176         if (_enhancedDOM != null) {
177             int ident = _enhancedDOM.getElementById(id);
178 
179             if (ident != DTM.NULL) {
180                 Integer root = new Integer(_enhancedDOM.getDocument());
181                 Hashtable index = (Hashtable) _rootToIndexMap.get(root);
182 
183                 if (index == null) {
184                     index = new Hashtable();
185                     _rootToIndexMap.put(root, index);
186                 } else {
187                     nodes = (IntegerArray) index.get(id);
188                 }
189 
190                 if (nodes == null) {
191                     nodes = new IntegerArray();
192                     index.put(id, nodes);
193                 }
194 
195                 nodes.add(_enhancedDOM.getNodeHandle(ident));
196             }
197         }
198 
199         return nodes;
200     }
201 
202     /**
203      * <p>This method must be called by the code generated by the key() function
204      * prior to returning the node iterator.</p>
205      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
206      * <b>deprecated.</b></em></p>
207      * @deprecated
208      */
209     public void lookupKey(Object value) {
210         IntegerArray nodes = (IntegerArray) _index.get(value);
211         _nodes = (nodes != null) ? (IntegerArray) nodes.clone() : null;
212         _position = 0;
213     }
214 
215     /**
216      * <p>Callers should not call next() after it returns END.</p>
217      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
218      * <b>deprecated.</b></em></p>
219      * @deprecated
220      */
221     public int next() {
222         if (_nodes == null) return DTMAxisIterator.END;
223 
224         return (_position < _nodes.cardinality()) ?
225             _dom.getNodeHandle(_nodes.at(_position++)) : DTMAxisIterator.END;
226     }
227 
228     /**
229      * Given a context node and the argument to the XPath <code>id</code>
230      * function, checks whether the context node is in the set of nodes that
231      * results from that reference to the <code>id</code> function.  This is
232      * used in the implementation of <code>id</code> patterns.
233      *
234      * @param node The context node
235      * @param value The argument to the <code>id</code> function
236      * @return <code>1</code> if the context node is in the set of nodes
237      *         returned by the reference to the <code>id</code> function;
238      *         <code>0</code>, otherwise
239      */
240     public int containsID(int node, Object value) {
241         final String string = (String)value;
242         int rootHandle = _dom.getAxisIterator(Axis.ROOT)
243                                  .setStartNode(node).next();
244 
245         // Get the mapping table for the document containing the context node
246         Hashtable index =
247             (Hashtable) _rootToIndexMap.get(new Integer(rootHandle));
248 
249         // Split argument to id function into XML whitespace separated tokens
250         final StringTokenizer values = new StringTokenizer(string, " \n\t");
251 
252         while (values.hasMoreElements()) {
253             final String token = (String) values.nextElement();
254             IntegerArray nodes = null;
255 
256             if (index != null) {
257                 nodes = (IntegerArray) index.get(token);
258             }
259 
260             // If input was from W3C DOM, use DOM's getElementById to do
261             // the look-up.
262             if (nodes == null && _enhancedDOM != null
263                 && _enhancedDOM.hasDOMSource()) {
264                 nodes = getDOMNodeById(token);
265             }
266 
267             // Did we find the context node in the set of nodes?
268             if (nodes != null && nodes.indexOf(node) >= 0) {
269                 return 1;
270             }
271         }
272 
273         // Didn't find the context node in the set of nodes returned by id
274         return 0;
275     }
276 
277     /**
278      * <p>Given a context node and the second argument to the XSLT
279      * <code>key</code> function, checks whether the context node is in the
280      * set of nodes that results from that reference to the <code>key</code>
281      * function.  This is used in the implementation of key patterns.</p>
282      * <p>This particular {@link KeyIndex} object is the result evaluating the
283      * first argument to the <code>key</code> function, so it's not taken into
284      * any further account.</p>
285      *
286      * @param node The context node
287      * @param value The second argument to the <code>key</code> function
288      * @return <code>1</code> if and only if the context node is in the set of
289      *         nodes returned by the reference to the <code>key</code> function;
290      *         <code>0</code>, otherwise
291      */
292     public int containsKey(int node, Object value) {
293         int rootHandle = _dom.getAxisIterator(Axis.ROOT)
294                                  .setStartNode(node).next();
295 
296         // Get the mapping table for the document containing the context node
297         Hashtable index =
298                     (Hashtable) _rootToIndexMap.get(new Integer(rootHandle));
299 
300         // Check whether the context node is present in the set of nodes
301         // returned by the key function
302         if (index != null) {
303             final IntegerArray nodes = (IntegerArray) index.get(value);
304             return (nodes != null && nodes.indexOf(node) >= 0) ? 1 : 0;
305         }
306 
307         // The particular key name identifies no nodes in this document
308         return 0;
309     }
310 
311     /**
312      * <p>Resets the iterator to the last start node.</p>
313      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
314      * <b>deprecated.</b></em></p>
315      * @deprecated
316      */
317     public DTMAxisIterator reset() {
318         _position = 0;
319         return this;
320     }
321 
322     /**
323      * <p>Returns the number of elements in this iterator.</p>
324      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
325      * <b>deprecated.</b></em></p>
326      * @deprecated
327      */
328     public int getLast() {
329         return (_nodes == null) ? 0 : _nodes.cardinality();
330     }
331 
332     /**
333      * <p>Returns the position of the current node in the set.</p>
334      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
335      * <b>deprecated.</b></em></p>
336      * @deprecated
337      */
338     public int getPosition() {
339         return _position;
340     }
341 
342     /**
343      * <p>Remembers the current node for the next call to gotoMark().</p>
344      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
345      * <b>deprecated.</b></em></p>
346      * @deprecated
347      */
348     public void setMark() {
349         _markedPosition = _position;
350     }
351 
352     /**
353      * <p>Restores the current node remembered by setMark().</p>
354      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
355      * <b>deprecated.</b></em></p>
356      * @deprecated
357      */
358     public void gotoMark() {
359         _position = _markedPosition;
360     }
361 
362     /**
363      * <p>Set start to END should 'close' the iterator,
364      * i.e. subsequent call to next() should return END.</p>
365      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
366      * <b>deprecated.</b></em></p>
367      * @deprecated
368      */
369     public DTMAxisIterator setStartNode(int start) {
370         if (start == DTMAxisIterator.END) {
371             _nodes = null;
372         }
373         else if (_nodes != null) {
374             _position = 0;
375         }
376         return (DTMAxisIterator) this;
377     }
378 
379     /**
380      * <p>Get start to END should 'close' the iterator,
381      * i.e. subsequent call to next() should return END.</p>
382      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
383      * <b>deprecated.</b></em></p>
384      * @deprecated
385      */
386     public int getStartNode() {
387         return 0;
388     }
389 
390     /**
391      * <p>True if this iterator has a reversed axis.</p>
392      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
393      * <b>deprecated.</b></em></p>
394      * @deprecated
395      */
396     public boolean isReverse() {
397         return(false);
398     }
399 
400     /**
401      * <p>Returns a deep copy of this iterator.</p>
402      * <p><em>Use of an instance of this class as a {@link DTMAxisIterator} is
403      * <b>deprecated.</b></em></p>
404      * @deprecated
405      */
406     public DTMAxisIterator cloneIterator() {
407         KeyIndex other = new KeyIndex(0);
408         other._index = _index;
409         other._rootToIndexMap = _rootToIndexMap;
410         other._nodes = _nodes;
411         other._position = _position;
412         return (DTMAxisIterator) other;
413     }
414 
415     public void setDom(DOM dom, int node) {
416         _dom = dom;
417 
418         // If a MultiDOM, ensure _enhancedDOM is correctly set
419         // so that getElementById() works in lookupNodes below
420         if (dom instanceof MultiDOM) {
421             dom = ((MultiDOM) dom).getDTM(node);
422         }
423 
424         if (dom instanceof DOMEnhancedForDTM) {
425             _enhancedDOM = (DOMEnhancedForDTM)dom;
426         }
427         else if (dom instanceof DOMAdapter) {
428             DOM idom = ((DOMAdapter)dom).getDOMImpl();
429             if (idom instanceof DOMEnhancedForDTM) {
430                 _enhancedDOM = (DOMEnhancedForDTM)idom;
431             }
432         }
433     }
434 
435     /**
436      * Create a {@link KeyIndexIterator} that iterates over the nodes that
437      * result from a reference to the XSLT <code>key</code> function or
438      * XPath <code>id</code> function.
439      *
440      * @param keyValue A string or iterator representing the key values or id
441      *                 references
442      * @param isKeyCall A <code>boolean</code> indicating whether the iterator
443      *                 is being created for a reference <code>key</code> or
444      *                 <code>id</code>
445      */
446     public KeyIndexIterator getKeyIndexIterator(Object keyValue,
447                                                 boolean isKeyCall) {
448         if (keyValue instanceof DTMAxisIterator) {
449             return getKeyIndexIterator((DTMAxisIterator) keyValue, isKeyCall);
450         } else {
451             return getKeyIndexIterator(BasisLibrary.stringF(keyValue, _dom),
452                                        isKeyCall);
453         }
454     }
455 
456     /**
457      * Create a {@link KeyIndexIterator} that iterates over the nodes that
458      * result from a reference to the XSLT <code>key</code> function or
459      * XPath <code>id</code> function.
460      *
461      * @param keyValue A string representing the key values or id
462      *                 references
463      * @param isKeyCall A <code>boolean</code> indicating whether the iterator
464      *                 is being created for a reference <code>key</code> or
465      *                 <code>id</code>
466      */
467     public KeyIndexIterator getKeyIndexIterator(String keyValue,
468                                                 boolean isKeyCall) {
469         return new KeyIndexIterator(keyValue, isKeyCall);
470     }
471 
472     /**
473      * Create a {@link KeyIndexIterator} that iterates over the nodes that
474      * result from a reference to the XSLT <code>key</code> function or
475      * XPath <code>id</code> function.
476      *
477      * @param keyValue An iterator representing the key values or id
478      *                 references
479      * @param isKeyCall A <code>boolean</code> indicating whether the iterator
480      *                 is being created for a reference <code>key</code> or
481      *                 <code>id</code>
482      */
483     public KeyIndexIterator getKeyIndexIterator(DTMAxisIterator keyValue,
484                                                 boolean isKeyCall) {
485         return new KeyIndexIterator(keyValue, isKeyCall);
486     }
487 
488     /**
489      * Used to represent an empty node set.
490      */
491     final private static IntegerArray EMPTY_NODES = new IntegerArray(0);
492 
493 
494     /**
495      * An iterator representing the result of a reference to either the
496      * XSLT <code>key</code> function or the XPath <code>id</code> function.
497      */
498     public class KeyIndexIterator extends MultiValuedNodeHeapIterator {
499 
500         /**
501          * <p>A reference to the <code>key</code> function that only has one
502          * key value or to the <code>id</code> function that has only one string
503          * argument can be optimized to ignore the multi-valued heap.  This
504          * field will be <code>null</code> otherwise.
505          */
506         private IntegerArray _nodes;
507 
508         /**
509          * <p>This field contains the iterator representing a node set key value
510          * argument to the <code>key</code> function or a node set argument
511          * to the <code>id</code> function.</p>
512          *
513          * <p>Exactly one of this field and {@link #_keyValue} must be
514          * <code>null</code>.</p>
515          */
516         private DTMAxisIterator _keyValueIterator;
517 
518         /**
519          * <p>This field contains the iterator representing a non-node-set key
520          * value argument to the <code>key</code> function or a non-node-set
521          * argument to the <code>id</code> function.</p>
522          *
523          * <p>Exactly one of this field and {@link #_keyValueIterator} must be
524          * <code>null</code>.</p>
525          */
526         private String _keyValue;
527 
528         /**
529          * Indicates whether this object represents the result of a reference
530          * to the <code>key</code> function (<code>true</code>) or the
531          * <code>id</code> function (<code>false</code>).
532          */
533         private boolean _isKeyIterator;
534 
535         /**
536          * Represents the DTM nodes retrieved for one key value or one string
537          * argument to <code>id</code> for use as one heap node in a
538          * {@link MultiValuedNodeHeapIterator}.
539          */
540         protected class KeyIndexHeapNode
541                 extends MultiValuedNodeHeapIterator.HeapNode
542         {
543             /**
544              * {@link IntegerArray} of DTM nodes retrieved for one key value.
545              * Must contain no duplicates and be stored in document order.
546              */
547             private IntegerArray _nodes;
548 
549             /**
550              * Position in {@link #_nodes} array of next node to return from
551              * this heap node.
552              */
553             private int _position = 0;
554 
555             /**
556              * Marked position.  Used by {@link #setMark()} and
557              * {@link #gotoMark()}
558              */
559             private int _markPosition = -1;
560 
561             /**
562              * Create a heap node representing DTM nodes retrieved for one
563              * key value in a reference to the <code>key</code> function
564              * or string argument to the <code>id</code> function.
565              */
566             KeyIndexHeapNode(IntegerArray nodes) {
567                 _nodes = nodes;
568             }
569 
570             /**
571              * Advance to the next node represented by this {@link HeapNode}
572              *
573              * @return the next DTM node.
574              */
575             public int step() {
576                 if (_position < _nodes.cardinality()) {
577                     _node = _nodes.at(_position);
578                     _position++;
579                 } else {
580                     _node = DTMAxisIterator.END;
581                 }
582 
583                 return _node;
584             }
585 
586             /**
587              * Creates a deep copy of this {@link HeapNode}.  The clone is not
588              * reset from the current position of the original.
589              *
590              * @return the cloned heap node
591              */
592             public HeapNode cloneHeapNode() {
593                 KeyIndexHeapNode clone =
594                         (KeyIndexHeapNode) super.cloneHeapNode();
595 
596                 clone._nodes = _nodes;
597                 clone._position = _position;
598                 clone._markPosition = _markPosition;
599 
600                 return clone;
601             }
602 
603             /**
604              * Remembers the current node for the next call to
605              * {@link #gotoMark()}.
606              */
607             public void setMark() {
608                 _markPosition = _position;
609             }
610 
611             /**
612              * Restores the current node remembered by {@link #setMark()}.
613              */
614             public void gotoMark() {
615                 _position = _markPosition;
616             }
617 
618             /**
619              * Performs a comparison of the two heap nodes
620              *
621              * @param heapNode the heap node against which to compare
622              * @return <code>true</code> if and only if the current node for
623              *         this heap node is before the current node of the
624              *         argument heap node in document order.
625              */
626             public boolean isLessThan(HeapNode heapNode) {
627                 return _node < heapNode._node;
628             }
629 
630             /**
631              * <p>Sets context with respect to which this heap node is
632              * evaluated.</p>
633              * <p>This has no real effect on this kind of heap node.  Instead,
634              * the {@link KeyIndexIterator#setStartNode(int)} method should
635              * create new instances of this class to represent the effect of
636              * changing the context.</p>
637              */
638             public HeapNode setStartNode(int node) {
639                 return this;
640             }
641 
642             /**
643              * Reset the heap node back to its beginning.
644              */
645             public HeapNode reset() {
646                 _position = 0;
647                 return this;
648             }
649         }
650 
651         /**
652          * Constructor used when the argument to <code>key</code> or
653          * <code>id</code> is not a node set.
654          *
655          * @param keyValue the argument to <code>key</code> or <code>id</code>
656          *                 cast to a <code>String</code>
657          * @param isKeyIterator indicates whether the constructed iterator
658          *                represents a reference to <code>key</code> or
659          *                <code>id</code>.
660          */
661         KeyIndexIterator(String keyValue, boolean isKeyIterator) {
662             _isKeyIterator = isKeyIterator;
663             _keyValue = keyValue;
664         }
665 
666         /**
667          * Constructor used when the argument to <code>key</code> or
668          * <code>id</code> is a node set.
669          *
670          * @param keyValues the argument to <code>key</code> or <code>id</code>
671          * @param isKeyIterator indicates whether the constructed iterator
672          *                represents a reference to <code>key</code> or
673          *                <code>id</code>.
674          */
675         KeyIndexIterator(DTMAxisIterator keyValues, boolean isKeyIterator) {
676             _keyValueIterator = keyValues;
677             _isKeyIterator = isKeyIterator;
678         }
679 
680         /**
681          * Retrieve nodes for a particular key value or a particular id
682          * argument value.
683          *
684          * @param root The root node of the document containing the context node
685          * @param keyValue The key value of id string argument value
686          * @return an {@link IntegerArray} of the resulting nodes
687          */
688         protected IntegerArray lookupNodes(int root, String keyValue) {
689             IntegerArray result = null;
690 
691             // Get mapping from key values/IDs to DTM nodes for this document
692             Hashtable index = (Hashtable)_rootToIndexMap.get(new Integer(root));
693 
694             if (!_isKeyIterator) {
695                 // For id function, tokenize argument as whitespace separated
696                 // list of values and look up nodes identified by each ID.
697                 final StringTokenizer values =
698                         new StringTokenizer(keyValue, " \n\t");
699 
700                 while (values.hasMoreElements()) {
701                     final String token = (String) values.nextElement();
702                     IntegerArray nodes = null;
703 
704                     // Does the ID map to any node in the document?
705                     if (index != null) {
706                         nodes = (IntegerArray) index.get(token);
707                     }
708 
709                     // If input was from W3C DOM, use DOM's getElementById to do
710                     // the look-up.
711                     if (nodes == null && _enhancedDOM != null
712                             && _enhancedDOM.hasDOMSource()) {
713                         nodes = getDOMNodeById(token);
714                     }
715 
716                     // If we found any nodes, merge them into the cumulative
717                     // result
718                     if (nodes != null) {
719                         if (result == null) {
720                             result = (IntegerArray)nodes.clone();
721                         } else {
722                             result.merge(nodes);
723                         }
724                     }
725                 }
726             } else if (index != null) {
727                 // For key function, map key value to nodes
728                 result = (IntegerArray) index.get(keyValue);
729             }
730 
731             return result;
732         }
733 
734         /**
735          * Set context node for the iterator.  This will cause the iterator
736          * to reset itself, reevaluate arguments to the function, look up
737          * nodes in the input and reinitialize its internal heap.
738          *
739          * @param node the context node
740          * @return A {@link DTMAxisIterator} set to the start of the iteration.
741          */
742         public DTMAxisIterator setStartNode(int node) {
743             _startNode = node;
744 
745             // If the arugment to the function is a node set, set the
746             // context node on it.
747             if (_keyValueIterator != null) {
748                 _keyValueIterator = _keyValueIterator.setStartNode(node);
749             }
750 
751             init();
752 
753             return super.setStartNode(node);
754         }
755 
756         /**
757          * Get the next node in the iteration.
758          *
759          * @return The next node handle in the iteration, or END.
760          */
761         public int next() {
762             int nodeHandle;
763 
764             // If at most one key value or at most one string argument to id
765             // resulted in nodes being returned, use the IntegerArray
766             // stored at _nodes directly.  This relies on the fact that the
767             // IntegerArray never includes duplicate nodes and is always stored
768             // in document order.
769             if (_nodes != null) {
770                 if (_position < _nodes.cardinality()) {
771                     nodeHandle = returnNode(_nodes.at(_position));
772                 } else {
773                     nodeHandle = DTMAxisIterator.END;
774                 }
775             } else {
776                 nodeHandle = super.next();
777             }
778 
779             return nodeHandle;
780         }
781 
782         /**
783          * Resets the iterator to the last start node.
784          *
785          * @return A DTMAxisIterator, which may or may not be the same as this
786          *         iterator.
787          */
788         public DTMAxisIterator reset() {
789             if (_nodes == null) {
790                 init();
791             } else {
792                 super.reset();
793             }
794 
795             return resetPosition();
796         }
797 
798         /**
799          * Evaluate the reference to the <code>key</code> or <code>id</code>
800          * function with the context specified by {@link #setStartNode(int)}
801          * and set up this iterator to iterate over the DTM nodes that are
802          * to be returned.
803          */
804         protected void init() {
805             super.init();
806             _position = 0;
807 
808             // All nodes retrieved are in the same document
809             int rootHandle = _dom.getAxisIterator(Axis.ROOT)
810                                       .setStartNode(_startNode).next();
811 
812             // Is the argument not a node set?
813             if (_keyValueIterator == null) {
814                 // Look up nodes returned for the single string argument
815                 _nodes = lookupNodes(rootHandle, _keyValue);
816 
817                 if (_nodes == null) {
818                     _nodes = EMPTY_NODES;
819                 }
820             } else {
821                 DTMAxisIterator keyValues = _keyValueIterator.reset();
822                 int retrievedKeyValueIdx = 0;
823                 boolean foundNodes = false;
824 
825                 _nodes = null;
826 
827                 // For each node in the node set argument, get the string value
828                 // and look up the nodes returned by key or id for that string
829                 // value.  If at most one string value has nodes associated,
830                 // the nodes will be stored in _nodes; otherwise, the nodes
831                 // will be placed in a heap.
832                 for (int keyValueNode = keyValues.next();
833                      keyValueNode != DTMAxisIterator.END;
834                      keyValueNode = keyValues.next()) {
835 
836                     String keyValue = BasisLibrary.stringF(keyValueNode, _dom);
837 
838                     IntegerArray nodes = lookupNodes(rootHandle, keyValue);
839 
840                     if (nodes != null) {
841                         if (!foundNodes) {
842                             _nodes = nodes;
843                             foundNodes = true;
844                         } else {
845                             if (_nodes != null) {
846                                 addHeapNode(new KeyIndexHeapNode(_nodes));
847                                 _nodes = null;
848                             }
849                             addHeapNode(new KeyIndexHeapNode(nodes));
850                         }
851                     }
852                 }
853 
854                 if (!foundNodes) {
855                     _nodes = EMPTY_NODES;
856                 }
857             }
858         }
859 
860         /**
861          * Returns the number of nodes in this iterator.
862          *
863          * @return the number of nodes
864          */
865         public int getLast() {
866             // If nodes are stored in _nodes, take advantage of the fact that
867             // there are no duplicates.  Otherwise, fall back to the base heap
868             // implementaiton and hope it does a good job with this.
869             return (_nodes != null) ? _nodes.cardinality() : super.getLast();
870         }
871 
872         /**
873          * Return the node at the given position.
874          *
875          * @param position The position
876          * @return The node at the given position.
877          */
878         public int getNodeByPosition(int position) {
879             int node = DTMAxisIterator.END;
880 
881             // If nodes are stored in _nodes, take advantage of the fact that
882             // there are no duplicates and they are stored in document order.
883             // Otherwise, fall back to the base heap implementation to do a
884             // good job with this.
885             if (_nodes != null) {
886                 if (position > 0) {
887                     if (position <= _nodes.cardinality()) {
888                         _position = position;
889                         node = _nodes.at(position-1);
890                     } else {
891                         _position = _nodes.cardinality();
892                     }
893                 }
894             } else {
895                 node = super.getNodeByPosition(position);
896             }
897 
898             return node;
899         }
900     }
901 }