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