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 }