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 007 * "License"); you may not use this file except in compliance 008 * with the License. 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, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.mavibot.btree; 021 022 023import java.io.IOException; 024import java.util.NoSuchElementException; 025 026import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException; 027 028 029/** 030 * A Cursor is used to fetch only keys in a BTree and is returned by the 031 * @see BTree#browseKeys method. The cursor <strng>must</strong> be closed 032 * when the user is done with it. 033 * <p> 034 * 035 * @param <K> The type for the Key 036 * 037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 038 */ 039public class KeyCursor<K> 040{ 041 /** A marker to tell that we are before the first element */ 042 private static final int BEFORE_FIRST = -1; 043 044 /** A marker to tell that we are after the last element */ 045 private static final int AFTER_LAST = -2; 046 047 /** The stack of pages from the root down to the leaf */ 048 protected ParentPos<K, K>[] stack; 049 050 /** The stack's depth */ 051 protected int depth = 0; 052 053 /** The transaction used for this cursor */ 054 protected ReadTransaction<K, K> transaction; 055 056 057 /** 058 * Creates a new instance of Cursor. 059 */ 060 protected KeyCursor() 061 { 062 } 063 064 065 /** 066 * Creates a new instance of Cursor, starting on a page at a given position. 067 * 068 * @param transaction The transaction this operation is protected by 069 * @param stack The stack of parent's from root to this page 070 */ 071 public KeyCursor( ReadTransaction<K, K> transaction, ParentPos<K, K>[] stack, int depth ) 072 { 073 this.transaction = transaction; 074 this.stack = stack; 075 this.depth = depth; 076 } 077 078 079 /** 080 * Change the position in the current cursor to set it after the last key 081 */ 082 public void afterLast() throws IOException 083 { 084 // First check that we have elements in the BTree 085 if ( ( stack == null ) || ( stack.length == 0 ) ) 086 { 087 return; 088 } 089 090 Page<K, K> child = null; 091 092 for ( int i = 0; i < depth; i++ ) 093 { 094 ParentPos<K, K> parentPos = stack[i]; 095 096 if ( child != null ) 097 { 098 parentPos.page = child; 099 parentPos.pos = child.getNbElems(); 100 } 101 else 102 { 103 // We have N+1 children if the page is a Node, so we don't decrement the nbElems field 104 parentPos.pos = parentPos.page.getNbElems(); 105 } 106 107 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos ); 108 } 109 110 // and leaf 111 ParentPos<K, K> parentPos = stack[depth]; 112 113 if ( child == null ) 114 { 115 parentPos.pos = parentPos.page.getNbElems() - 1; 116 } 117 else 118 { 119 parentPos.page = child; 120 parentPos.pos = child.getNbElems() - 1; 121 } 122 123 parentPos.pos = AFTER_LAST; 124 } 125 126 127 /** 128 * Change the position in the current cursor before the first key 129 */ 130 public void beforeFirst() throws IOException 131 { 132 // First check that we have elements in the BTree 133 if ( ( stack == null ) || ( stack.length == 0 ) ) 134 { 135 return; 136 } 137 138 Page<K, K> child = null; 139 140 for ( int i = 0; i < depth; i++ ) 141 { 142 ParentPos<K, K> parentPos = stack[i]; 143 parentPos.pos = 0; 144 145 if ( child != null ) 146 { 147 parentPos.page = child; 148 } 149 150 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( 0 ); 151 } 152 153 // and leaf 154 ParentPos<K, K> parentPos = stack[depth]; 155 parentPos.pos = BEFORE_FIRST; 156 157 if ( child != null ) 158 { 159 parentPos.page = child; 160 } 161 } 162 163 164 /** 165 * Tells if the cursor can return a next element 166 * 167 * @return true if there are some more elements 168 * @throws IOException 169 * @throws EndOfFileExceededException 170 */ 171 public boolean hasNext() throws EndOfFileExceededException, IOException 172 { 173 // First check that we have elements in the BTree 174 if ( ( stack == null ) || ( stack.length == 0 ) ) 175 { 176 return false; 177 } 178 179 // Take the leaf and check if we have no mare keys 180 ParentPos<K, K> parentPos = stack[depth]; 181 182 if ( parentPos.page == null ) 183 { 184 // Empty BTree, get out 185 return false; 186 } 187 188 if ( parentPos.pos == AFTER_LAST ) 189 { 190 return false; 191 } 192 193 if ( parentPos.pos == BEFORE_FIRST ) 194 { 195 return true; 196 } 197 198 if ( parentPos.pos < parentPos.page.getNbElems() - 1 ) 199 { 200 // Not the last position, we have a next key 201 return true; 202 } 203 else 204 { 205 // Ok, here, we have reached the last key in the leaf. We have to go up and 206 // see if we have some remaining keys 207 int currentDepth = depth - 1; 208 209 while ( currentDepth >= 0 ) 210 { 211 parentPos = stack[currentDepth]; 212 213 if ( parentPos.pos < parentPos.page.getNbElems() ) 214 { 215 // The parent has some remaining keys on the right, get out 216 return true; 217 } 218 else 219 { 220 currentDepth--; 221 } 222 } 223 224 // We are done, there are no more key left 225 return false; 226 } 227 } 228 229 230 /** 231 * Find the next key 232 * 233 * @return the found key 234 * @throws IOException 235 * @throws EndOfFileExceededException 236 */ 237 public K next() throws EndOfFileExceededException, IOException 238 { 239 // First check that we have elements in the BTree 240 if ( ( stack == null ) || ( stack.length == 0 ) ) 241 { 242 throw new NoSuchElementException( "No Key is present" ); 243 } 244 245 ParentPos<K, K> parentPos = stack[depth]; 246 247 if ( ( parentPos.page == null ) || ( parentPos.pos == AFTER_LAST ) ) 248 { 249 // This is the end : no more keys 250 throw new NoSuchElementException( "No more keys present" ); 251 } 252 253 if ( parentPos.pos == parentPos.page.getNbElems() ) 254 { 255 // End of the leaf. We have to go back into the stack up to the 256 // parent, and down to the leaf 257 parentPos = findNextParentPos(); 258 259 // we also need to check for the type of page cause 260 // findNextParentPos will never return a null ParentPos 261 if ( ( parentPos == null ) || ( parentPos.page == null ) ) 262 { 263 // This is the end : no more keys 264 throw new NoSuchElementException( "No more keys present" ); 265 } 266 } 267 268 // Deal with the BeforeFirst case 269 if ( parentPos.pos == BEFORE_FIRST ) 270 { 271 parentPos.pos++; 272 } 273 else 274 { 275 if ( parentPos.pos == parentPos.page.getNbElems() - 1 ) 276 { 277 parentPos = findNextParentPos(); 278 279 if ( ( parentPos == null ) || ( parentPos.page == null ) ) 280 { 281 // This is the end : no more keys 282 throw new NoSuchElementException( "No more keys present" ); 283 } 284 } 285 else 286 { 287 parentPos.pos++; 288 } 289 } 290 291 AbstractPage<K, K> leaf = ( AbstractPage<K, K> ) ( parentPos.page ); 292 293 return leaf.getKey( parentPos.pos ); 294 } 295 296 297 /** 298 * Get the next key. 299 */ 300 public K nextKey() throws EndOfFileExceededException, IOException 301 { 302 return next(); 303 } 304 305 306 /** 307 * Tells if the cursor can return a next key 308 * 309 * @return true if there are some more keys 310 * @throws IOException 311 * @throws EndOfFileExceededException 312 */ 313 public boolean hasNextKey() throws EndOfFileExceededException, IOException 314 { 315 // First check that we have elements in the BTree 316 if ( ( stack == null ) || ( stack.length == 0 ) ) 317 { 318 // This is the end : no more key 319 return false; 320 } 321 322 ParentPos<K, K> parentPos = stack[depth]; 323 324 if ( parentPos.page == null ) 325 { 326 // This is the end : no more key 327 return false; 328 } 329 330 if ( parentPos.pos == ( parentPos.page.getNbElems() - 1 ) ) 331 { 332 // End of the leaf. We have to go back into the stack up to the 333 // parent, and down to the next leaf 334 return hasNextParentPos(); 335 } 336 else 337 { 338 return true; 339 } 340 } 341 342 343 /** 344 * Tells if the cursor can return a previous element 345 * 346 * @return true if there are some more elements 347 * @throws IOException 348 * @throws EndOfFileExceededException 349 */ 350 public boolean hasPrev() throws EndOfFileExceededException, IOException 351 { 352 // First check that we have elements in the BTree 353 if ( ( stack == null ) || ( stack.length == 0 ) ) 354 { 355 return false; 356 } 357 358 // Take the leaf and check if we have no mare keys 359 ParentPos<K, K> parentPos = stack[depth]; 360 361 if ( parentPos.page == null ) 362 { 363 // Empty BTree, get out 364 return false; 365 } 366 367 if ( parentPos.pos > 0 ) 368 { 369 // get out, we have keys on the left 370 return true; 371 } 372 else 373 { 374 // Check that we are not before the first key 375 if ( parentPos.pos == BEFORE_FIRST ) 376 { 377 return false; 378 } 379 380 if ( parentPos.pos == AFTER_LAST ) 381 { 382 return true; 383 } 384 385 // Ok, here, we have reached the first key in the leaf. We have to go up and 386 // see if we have some remaining keys 387 int currentDepth = depth - 1; 388 389 while ( currentDepth >= 0 ) 390 { 391 parentPos = stack[currentDepth]; 392 393 if ( parentPos.pos > 0 ) 394 { 395 // The parent has some remaining keys on the right, get out 396 return true; 397 } 398 else 399 { 400 currentDepth--; 401 } 402 } 403 404 return false; 405 } 406 } 407 408 409 /** 410 * Find the previous key 411 * 412 * @return the found key 413 * @throws IOException 414 * @throws EndOfFileExceededException 415 */ 416 public K prev() throws EndOfFileExceededException, IOException 417 { 418 // First check that we have elements in the BTree 419 if ( ( stack == null ) || ( stack.length == 0 ) ) 420 { 421 throw new NoSuchElementException( "No more keys present" ); 422 } 423 424 ParentPos<K, K> parentPos = stack[depth]; 425 426 if ( ( parentPos.page == null ) || ( parentPos.pos == BEFORE_FIRST ) ) 427 { 428 // This is the end : no more keys 429 throw new NoSuchElementException( "No more keys present" ); 430 } 431 432 // Deal with the AfterLast case 433 if ( parentPos.pos == AFTER_LAST ) 434 { 435 parentPos.pos = parentPos.page.getNbElems() - 1; 436 } 437 else 438 { 439 if ( parentPos.pos == 0 ) 440 { 441 parentPos = findPrevParentPos(); 442 443 if ( ( parentPos == null ) || ( parentPos.page == null ) ) 444 { 445 // This is the end : no more keys 446 throw new NoSuchElementException( "No more keys present" ); 447 } 448 } 449 else 450 { 451 parentPos.pos--; 452 } 453 } 454 455 AbstractPage<K, K> leaf = ( AbstractPage<K, K> ) ( parentPos.page ); 456 457 return leaf.getKey( parentPos.pos ); 458 } 459 460 461 /** 462 * Get the previous key. 463 * 464 * @return the found key 465 * @throws EndOfFileExceededException 466 * @throws IOException 467 */ 468 public K prevKey() throws EndOfFileExceededException, IOException 469 { 470 return prev(); 471 } 472 473 474 /** 475 * Tells if the cursor can return a previous key 476 * 477 * @return true if there are some more keys 478 * @throws IOException 479 * @throws EndOfFileExceededException 480 */ 481 public boolean hasPrevKey() throws EndOfFileExceededException, IOException 482 { 483 // First check that we have elements in the BTree 484 if ( ( stack == null ) || ( stack.length == 0 ) ) 485 { 486 // This is the end : no more key 487 return false; 488 } 489 490 ParentPos<K, K> parentPos = stack[depth]; 491 492 if ( parentPos.page == null ) 493 { 494 // This is the end : no more key 495 return false; 496 } 497 498 switch ( parentPos.pos ) 499 { 500 case 0 : 501 // Beginning of the leaf. We have to go back into the stack up to the 502 // parent, and down to the leaf 503 return hasPrevParentPos(); 504 505 case -1 : 506 // no previous key 507 return false; 508 509 default : 510 // we have a previous key 511 return true; 512 } 513 } 514 515 516 /** 517 * Tells if there is a next ParentPos 518 * 519 * @return the new ParentPos instance, or null if we have no following leaf 520 * @throws IOException 521 * @throws EndOfFileExceededException 522 */ 523 private boolean hasNextParentPos() throws EndOfFileExceededException, IOException 524 { 525 if ( depth == 0 ) 526 { 527 // No need to go any further, there is only one leaf in the btree 528 return false; 529 } 530 531 int currentDepth = depth - 1; 532 Page<K, K> child = null; 533 534 // First, go up the tree until we find a Node which has some element on the right 535 while ( currentDepth >= 0 ) 536 { 537 // We first go up the tree, until we reach a page whose current position 538 // is not the last one 539 ParentPos<K, K> parentPos = stack[currentDepth]; 540 541 if ( parentPos.pos + 1 > parentPos.page.getNbElems() ) 542 { 543 // No more element on the right : go up 544 currentDepth--; 545 } 546 else 547 { 548 // We can pick the next element at this level 549 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos + 1 ); 550 551 // and go down the tree through the nodes 552 while ( currentDepth < depth - 1 ) 553 { 554 currentDepth++; 555 child = ( ( AbstractPage<K, K> ) child ).getPage( 0 ); 556 } 557 558 return true; 559 } 560 } 561 562 return false; 563 } 564 565 566 /** 567 * Find the leaf containing the following elements. 568 * 569 * @return the new ParentPos instance, or null if we have no following leaf 570 * @throws IOException 571 * @throws EndOfFileExceededException 572 */ 573 private ParentPos<K, K> findNextParentPos() throws EndOfFileExceededException, IOException 574 { 575 if ( depth == 0 ) 576 { 577 // No need to go any further, there is only one leaf in the btree 578 return null; 579 } 580 581 int currentDepth = depth - 1; 582 Page<K, K> child = null; 583 584 // First, go up the tree until we find a Node which has some element on the right 585 while ( currentDepth >= 0 ) 586 { 587 // We first go up the tree, until we reach a page whose current position 588 // is not the last one 589 ParentPos<K, K> parentPos = stack[currentDepth]; 590 591 if ( parentPos.pos + 1 > parentPos.page.getNbElems() ) 592 { 593 // No more element on the right : go up 594 currentDepth--; 595 } 596 else 597 { 598 // We can pick the next element at this level 599 parentPos.pos++; 600 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos ); 601 602 // and go down the tree through the nodes 603 while ( currentDepth < depth - 1 ) 604 { 605 currentDepth++; 606 parentPos = stack[currentDepth]; 607 parentPos.pos = 0; 608 parentPos.page = child; 609 child = ( ( AbstractPage<K, K> ) child ).getPage( 0 ); 610 } 611 612 // and the leaf 613 parentPos = stack[depth]; 614 parentPos.page = child; 615 parentPos.pos = 0; 616 617 return parentPos; 618 } 619 } 620 621 return null; 622 } 623 624 625 /** 626 * Find the leaf containing the previous elements. 627 * 628 * @return the new ParentPos instance, or null if we have no previous leaf 629 * @throws IOException 630 * @throws EndOfFileExceededException 631 */ 632 private ParentPos<K, K> findPrevParentPos() throws EndOfFileExceededException, IOException 633 { 634 if ( depth == 0 ) 635 { 636 // No need to go any further, there is only one leaf in the btree 637 return null; 638 } 639 640 int currentDepth = depth - 1; 641 Page<K, K> child = null; 642 643 // First, go up the tree until we find a Node which has some element on the left 644 while ( currentDepth >= 0 ) 645 { 646 // We first go up the tree, until we reach a page whose current position 647 // is not the last one 648 ParentPos<K, K> parentPos = stack[currentDepth]; 649 650 if ( parentPos.pos == 0 ) 651 { 652 // No more element on the right : go up 653 currentDepth--; 654 } 655 else 656 { 657 // We can pick the next element at this level 658 parentPos.pos--; 659 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos ); 660 661 // and go down the tree through the nodes 662 while ( currentDepth < depth - 1 ) 663 { 664 currentDepth++; 665 parentPos = stack[currentDepth]; 666 parentPos.pos = child.getNbElems(); 667 parentPos.page = child; 668 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.page.getNbElems() ); 669 } 670 671 // and the leaf 672 parentPos = stack[depth]; 673 parentPos.pos = child.getNbElems() - 1; 674 parentPos.page = child; 675 676 return parentPos; 677 } 678 } 679 680 return null; 681 } 682 683 684 /** 685 * Tells if there is a prev ParentPos 686 * 687 * @return the new ParentPos instance, or null if we have no previous leaf 688 * @throws IOException 689 * @throws EndOfFileExceededException 690 */ 691 private boolean hasPrevParentPos() throws EndOfFileExceededException, IOException 692 { 693 if ( depth == 0 ) 694 { 695 // No need to go any further, there is only one leaf in the btree 696 return false; 697 } 698 699 int currentDepth = depth - 1; 700 Page<K, K> child = null; 701 702 // First, go up the tree until we find a Node which has some element on the right 703 while ( currentDepth >= 0 ) 704 { 705 // We first go up the tree, until we reach a page whose current position 706 // is not the last one 707 ParentPos<K, K> parentPos = stack[currentDepth]; 708 709 if ( parentPos.pos == 0 ) 710 { 711 // No more element on the left : go up 712 currentDepth--; 713 } 714 else 715 { 716 // We can pick the previous element at this level 717 child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos - 1 ); 718 719 // and go down the tree through the nodes 720 while ( currentDepth < depth - 1 ) 721 { 722 currentDepth++; 723 child = ( ( AbstractPage<K, K> ) child ).getPage( child.getNbElems() ); 724 } 725 726 return true; 727 } 728 } 729 730 return false; 731 } 732 733 734 /** 735 * {@inheritDoc} 736 */ 737 public void close() 738 { 739 transaction.close(); 740 } 741 742 743 /** 744 * Get the creation date 745 * @return The creation date for this cursor 746 */ 747 public long getCreationDate() 748 { 749 return transaction.getCreationDate(); 750 } 751 752 753 /** 754 * Get the current revision 755 * 756 * @return The revision this cursor is based on 757 */ 758 public long getRevision() 759 { 760 return transaction.getRevision(); 761 } 762 763 764 public String toString() 765 { 766 StringBuilder sb = new StringBuilder(); 767 768 sb.append( "KeyCursor, depth = " ).append( depth ).append( "\n" ); 769 770 for ( int i = 0; i <= depth; i++ ) 771 { 772 sb.append( " " ).append( stack[i] ).append( "\n" ); 773 } 774 775 return sb.toString(); 776 } 777}