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