1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: RangeCursor.java,v 1.8 2008/02/07 17:12:29 mark Exp $ 7 */ 8 9package com.sleepycat.util.keyrange; 10 11import com.sleepycat.compat.DbCompat; 12import com.sleepycat.db.Cursor; 13import com.sleepycat.db.DatabaseEntry; 14import com.sleepycat.db.DatabaseException; 15import com.sleepycat.db.LockMode; 16import com.sleepycat.db.OperationStatus; 17import com.sleepycat.db.SecondaryCursor; 18 19/** 20 * A cursor-like interface that enforces a key range. The method signatures 21 * are actually those of SecondaryCursor, but the pKey parameter may be null. 22 * It was done this way to avoid doubling the number of methods. 23 * 24 * <p>This is not a fully general implementation of a range cursor and should 25 * not be used directly by applications; however, it may evolve into a 26 * generally useful range cursor some day.</p> 27 * 28 * @author Mark Hayes 29 */ 30public class RangeCursor implements Cloneable { 31 32 /** 33 * The cursor and secondary cursor are the same object. The secCursor is 34 * null if the database is not a secondary database. 35 */ 36 private Cursor cursor; 37 private SecondaryCursor secCursor; 38 39 /** 40 * The range is always non-null, but may be unbounded meaning that it is 41 * open and not used. 42 */ 43 private KeyRange range; 44 45 /** 46 * The pkRange may be non-null only if the range is a single-key range 47 * and the cursor is a secondary cursor. It further restricts the range of 48 * primary keys in a secondary database. 49 */ 50 private KeyRange pkRange; 51 52 /** 53 * If the DB supported sorted duplicates, then calling 54 * Cursor.getSearchBothRange is allowed. 55 */ 56 private boolean sortedDups; 57 58 /** 59 * The privXxx entries are used only when the range is bounded. We read 60 * into these private entries to avoid modifying the caller's entry 61 * parameters in the case where we read successfully but the key is out of 62 * range. In that case we return NOTFOUND and we want to leave the entry 63 * parameters unchanged. 64 */ 65 private DatabaseEntry privKey; 66 private DatabaseEntry privPKey; 67 private DatabaseEntry privData; 68 69 /** 70 * The initialized flag is set to true whenever we successfully position 71 * the cursor. It is used to implement the getNext/Prev logic for doing a 72 * getFirst/Last when the cursor is not initialized. We can't rely on 73 * Cursor to do that for us, since if we position the underlying cursor 74 * successfully but the key is out of range, we have no way to set the 75 * underlying cursor to uninitialized. A range cursor always starts in the 76 * uninitialized state. 77 */ 78 private boolean initialized; 79 80 /** 81 * Creates a range cursor with a duplicate range. 82 */ 83 public RangeCursor(KeyRange range, 84 KeyRange pkRange, 85 boolean sortedDups, 86 Cursor cursor) 87 throws DatabaseException { 88 89 if (pkRange != null && !range.singleKey) { 90 throw new IllegalArgumentException(); 91 } 92 this.range = range; 93 this.pkRange = pkRange; 94 this.sortedDups = sortedDups; 95 this.cursor = cursor; 96 init(); 97 if (pkRange != null && secCursor == null) { 98 throw new IllegalArgumentException(); 99 } 100 } 101 102 /** 103 * Create a cloned range cursor. The caller must clone the underlying 104 * cursor before using this constructor, because cursor open/close is 105 * handled specially for CDS cursors outside this class. 106 */ 107 public RangeCursor dup(boolean samePosition) 108 throws DatabaseException { 109 110 try { 111 RangeCursor c = (RangeCursor) super.clone(); 112 c.cursor = dupCursor(cursor, samePosition); 113 c.init(); 114 return c; 115 } catch (CloneNotSupportedException neverHappens) { 116 return null; 117 } 118 } 119 120 /** 121 * Used for opening and duping (cloning). 122 */ 123 private void init() { 124 125 if (cursor instanceof SecondaryCursor) { 126 secCursor = (SecondaryCursor) cursor; 127 } else { 128 secCursor = null; 129 } 130 131 if (range.hasBound()) { 132 privKey = new DatabaseEntry(); 133 privPKey = new DatabaseEntry(); 134 privData = new DatabaseEntry(); 135 } else { 136 privKey = null; 137 privPKey = null; 138 privData = null; 139 } 140 } 141 142 /** 143 * Returns whether the cursor is initialized at a valid position. 144 */ 145 public boolean isInitialized() { 146 return initialized; 147 } 148 149 /** 150 * Returns the underlying cursor. Used for cloning. 151 */ 152 public Cursor getCursor() { 153 return cursor; 154 } 155 156 /** 157 * When an unbounded range is used, this method is called to use the 158 * callers entry parameters directly, to avoid the extra step of copying 159 * between the private entries and the caller's entries. 160 */ 161 private void setParams(DatabaseEntry key, DatabaseEntry pKey, 162 DatabaseEntry data) { 163 privKey = key; 164 privPKey = pKey; 165 privData = data; 166 } 167 168 /** 169 * Dups the cursor, sets the cursor and secCursor fields to the duped 170 * cursor, and returns the old cursor. Always call endOperation in a 171 * finally clause after calling beginOperation. 172 * 173 * <p>If the returned cursor == the cursor field, the cursor is 174 * uninitialized and was not duped; this case is handled correctly by 175 * endOperation.</p> 176 */ 177 private Cursor beginOperation() 178 throws DatabaseException { 179 180 Cursor oldCursor = cursor; 181 if (initialized) { 182 cursor = dupCursor(cursor, true); 183 if (secCursor != null) { 184 secCursor = (SecondaryCursor) cursor; 185 } 186 } else { 187 return cursor; 188 } 189 return oldCursor; 190 } 191 192 /** 193 * If the operation succeded, leaves the duped cursor in place and closes 194 * the oldCursor. If the operation failed, moves the oldCursor back in 195 * place and closes the duped cursor. oldCursor may be null if 196 * beginOperation was not called, in cases where we don't need to dup 197 * the cursor. Always call endOperation when a successful operation ends, 198 * in order to set the initialized field. 199 */ 200 private void endOperation(Cursor oldCursor, OperationStatus status, 201 DatabaseEntry key, DatabaseEntry pKey, 202 DatabaseEntry data) 203 throws DatabaseException { 204 205 if (status == OperationStatus.SUCCESS) { 206 if (oldCursor != null && oldCursor != cursor) { 207 closeCursor(oldCursor); 208 } 209 if (key != null) { 210 swapData(key, privKey); 211 } 212 if (pKey != null && secCursor != null) { 213 swapData(pKey, privPKey); 214 } 215 if (data != null) { 216 swapData(data, privData); 217 } 218 initialized = true; 219 } else { 220 if (oldCursor != null && oldCursor != cursor) { 221 closeCursor(cursor); 222 cursor = oldCursor; 223 if (secCursor != null) { 224 secCursor = (SecondaryCursor) cursor; 225 } 226 } 227 } 228 } 229 230 /** 231 * Swaps the contents of the two entries. Used to return entry data to 232 * the caller when the operation was successful. 233 */ 234 private static void swapData(DatabaseEntry e1, DatabaseEntry e2) { 235 236 byte[] d1 = e1.getData(); 237 int o1 = e1.getOffset(); 238 int s1 = e1.getSize(); 239 240 e1.setData(e2.getData(), e2.getOffset(), e2.getSize()); 241 e2.setData(d1, o1, s1); 242 } 243 244 /** 245 * Shares the same byte array, offset and size between two entries. 246 * Used when copying the entry data is not necessary because it is known 247 * that the underlying operation will not modify the entry, for example, 248 * with getSearchKey. 249 */ 250 private static void shareData(DatabaseEntry from, DatabaseEntry to) { 251 252 if (from != null) { 253 to.setData(from.getData(), from.getOffset(), from.getSize()); 254 } 255 } 256 257 public OperationStatus getFirst(DatabaseEntry key, 258 DatabaseEntry pKey, 259 DatabaseEntry data, 260 LockMode lockMode) 261 throws DatabaseException { 262 263 OperationStatus status; 264 if (!range.hasBound()) { 265 setParams(key, pKey, data); 266 status = doGetFirst(lockMode); 267 endOperation(null, status, null, null, null); 268 return status; 269 } 270 if (pkRange != null) { 271 KeyRange.copy(range.beginKey, privKey); 272 if (pkRange.singleKey) { 273 KeyRange.copy(pkRange.beginKey, privPKey); 274 status = doGetSearchBoth(lockMode); 275 endOperation(null, status, key, pKey, data); 276 } else { 277 status = OperationStatus.NOTFOUND; 278 Cursor oldCursor = beginOperation(); 279 try { 280 if (pkRange.beginKey == null || !sortedDups) { 281 status = doGetSearchKey(lockMode); 282 } else { 283 KeyRange.copy(pkRange.beginKey, privPKey); 284 status = doGetSearchBothRange(lockMode); 285 if (status == OperationStatus.SUCCESS && 286 !pkRange.beginInclusive && 287 pkRange.compare(privPKey, pkRange.beginKey) == 0) { 288 status = doGetNextDup(lockMode); 289 } 290 } 291 if (status == OperationStatus.SUCCESS && 292 !pkRange.check(privPKey)) { 293 status = OperationStatus.NOTFOUND; 294 } 295 } finally { 296 endOperation(oldCursor, status, key, pKey, data); 297 } 298 } 299 } else if (range.singleKey) { 300 KeyRange.copy(range.beginKey, privKey); 301 status = doGetSearchKey(lockMode); 302 endOperation(null, status, key, pKey, data); 303 } else { 304 status = OperationStatus.NOTFOUND; 305 Cursor oldCursor = beginOperation(); 306 try { 307 if (range.beginKey == null) { 308 status = doGetFirst(lockMode); 309 } else { 310 KeyRange.copy(range.beginKey, privKey); 311 status = doGetSearchKeyRange(lockMode); 312 if (status == OperationStatus.SUCCESS && 313 !range.beginInclusive && 314 range.compare(privKey, range.beginKey) == 0) { 315 status = doGetNextNoDup(lockMode); 316 } 317 } 318 if (status == OperationStatus.SUCCESS && 319 !range.check(privKey)) { 320 status = OperationStatus.NOTFOUND; 321 } 322 } finally { 323 endOperation(oldCursor, status, key, pKey, data); 324 } 325 } 326 return status; 327 } 328 329 public OperationStatus getLast(DatabaseEntry key, 330 DatabaseEntry pKey, 331 DatabaseEntry data, 332 LockMode lockMode) 333 throws DatabaseException { 334 335 OperationStatus status = OperationStatus.NOTFOUND; 336 if (!range.hasBound()) { 337 setParams(key, pKey, data); 338 status = doGetLast(lockMode); 339 endOperation(null, status, null, null, null); 340 return status; 341 } 342 Cursor oldCursor = beginOperation(); 343 try { 344 if (pkRange != null) { 345 KeyRange.copy(range.beginKey, privKey); 346 boolean doLast = false; 347 if (!sortedDups) { 348 status = doGetSearchKey(lockMode); 349 } else if (pkRange.endKey == null) { 350 doLast = true; 351 } else { 352 KeyRange.copy(pkRange.endKey, privPKey); 353 status = doGetSearchBothRange(lockMode); 354 if (status == OperationStatus.SUCCESS) { 355 if (!pkRange.endInclusive || 356 pkRange.compare(pkRange.endKey, privPKey) != 0) { 357 status = doGetPrevDup(lockMode); 358 } 359 } else { 360 KeyRange.copy(range.beginKey, privKey); 361 doLast = true; 362 } 363 } 364 if (doLast) { 365 status = doGetSearchKey(lockMode); 366 if (status == OperationStatus.SUCCESS) { 367 status = doGetNextNoDup(lockMode); 368 if (status == OperationStatus.SUCCESS) { 369 status = doGetPrev(lockMode); 370 } else { 371 status = doGetLast(lockMode); 372 } 373 } 374 } 375 if (status == OperationStatus.SUCCESS && 376 !pkRange.check(privPKey)) { 377 status = OperationStatus.NOTFOUND; 378 } 379 } else if (range.endKey == null) { 380 status = doGetLast(lockMode); 381 } else { 382 KeyRange.copy(range.endKey, privKey); 383 status = doGetSearchKeyRange(lockMode); 384 if (status == OperationStatus.SUCCESS) { 385 if (range.endInclusive && 386 range.compare(range.endKey, privKey) == 0) { 387 /* Skip this step if dups are not configured? */ 388 status = doGetNextNoDup(lockMode); 389 if (status == OperationStatus.SUCCESS) { 390 status = doGetPrev(lockMode); 391 } else { 392 status = doGetLast(lockMode); 393 } 394 } else { 395 status = doGetPrev(lockMode); 396 } 397 } else { 398 status = doGetLast(lockMode); 399 } 400 } 401 if (status == OperationStatus.SUCCESS && 402 !range.checkBegin(privKey, true)) { 403 status = OperationStatus.NOTFOUND; 404 } 405 } finally { 406 endOperation(oldCursor, status, key, pKey, data); 407 } 408 return status; 409 } 410 411 public OperationStatus getNext(DatabaseEntry key, 412 DatabaseEntry pKey, 413 DatabaseEntry data, 414 LockMode lockMode) 415 throws DatabaseException { 416 417 OperationStatus status; 418 if (!initialized) { 419 return getFirst(key, pKey, data, lockMode); 420 } 421 if (!range.hasBound()) { 422 setParams(key, pKey, data); 423 status = doGetNext(lockMode); 424 endOperation(null, status, null, null, null); 425 return status; 426 } 427 if (pkRange != null) { 428 if (pkRange.endKey == null) { 429 status = doGetNextDup(lockMode); 430 endOperation(null, status, key, pKey, data); 431 } else { 432 status = OperationStatus.NOTFOUND; 433 Cursor oldCursor = beginOperation(); 434 try { 435 status = doGetNextDup(lockMode); 436 if (status == OperationStatus.SUCCESS && 437 !pkRange.checkEnd(privPKey, true)) { 438 status = OperationStatus.NOTFOUND; 439 } 440 } finally { 441 endOperation(oldCursor, status, key, pKey, data); 442 } 443 } 444 } else if (range.singleKey) { 445 status = doGetNextDup(lockMode); 446 endOperation(null, status, key, pKey, data); 447 } else { 448 status = OperationStatus.NOTFOUND; 449 Cursor oldCursor = beginOperation(); 450 try { 451 status = doGetNext(lockMode); 452 if (status == OperationStatus.SUCCESS && 453 !range.check(privKey)) { 454 status = OperationStatus.NOTFOUND; 455 } 456 } finally { 457 endOperation(oldCursor, status, key, pKey, data); 458 } 459 } 460 return status; 461 } 462 463 public OperationStatus getNextNoDup(DatabaseEntry key, 464 DatabaseEntry pKey, 465 DatabaseEntry data, 466 LockMode lockMode) 467 throws DatabaseException { 468 469 OperationStatus status; 470 if (!initialized) { 471 return getFirst(key, pKey, data, lockMode); 472 } 473 if (!range.hasBound()) { 474 setParams(key, pKey, data); 475 status = doGetNextNoDup(lockMode); 476 endOperation(null, status, null, null, null); 477 return status; 478 } 479 if (range.singleKey) { 480 status = OperationStatus.NOTFOUND; 481 } else { 482 status = OperationStatus.NOTFOUND; 483 Cursor oldCursor = beginOperation(); 484 try { 485 status = doGetNextNoDup(lockMode); 486 if (status == OperationStatus.SUCCESS && 487 !range.check(privKey)) { 488 status = OperationStatus.NOTFOUND; 489 } 490 } finally { 491 endOperation(oldCursor, status, key, pKey, data); 492 } 493 } 494 return status; 495 } 496 497 public OperationStatus getPrev(DatabaseEntry key, 498 DatabaseEntry pKey, 499 DatabaseEntry data, 500 LockMode lockMode) 501 throws DatabaseException { 502 503 OperationStatus status; 504 if (!initialized) { 505 return getLast(key, pKey, data, lockMode); 506 } 507 if (!range.hasBound()) { 508 setParams(key, pKey, data); 509 status = doGetPrev(lockMode); 510 endOperation(null, status, null, null, null); 511 return status; 512 } 513 if (pkRange != null) { 514 if (pkRange.beginKey == null) { 515 status = doGetPrevDup(lockMode); 516 endOperation(null, status, key, pKey, data); 517 } else { 518 status = OperationStatus.NOTFOUND; 519 Cursor oldCursor = beginOperation(); 520 try { 521 status = doGetPrevDup(lockMode); 522 if (status == OperationStatus.SUCCESS && 523 !pkRange.checkBegin(privPKey, true)) { 524 status = OperationStatus.NOTFOUND; 525 } 526 } finally { 527 endOperation(oldCursor, status, key, pKey, data); 528 } 529 } 530 } else if (range.singleKey) { 531 status = doGetPrevDup(lockMode); 532 endOperation(null, status, key, pKey, data); 533 } else { 534 status = OperationStatus.NOTFOUND; 535 Cursor oldCursor = beginOperation(); 536 try { 537 status = doGetPrev(lockMode); 538 if (status == OperationStatus.SUCCESS && 539 !range.check(privKey)) { 540 status = OperationStatus.NOTFOUND; 541 } 542 } finally { 543 endOperation(oldCursor, status, key, pKey, data); 544 } 545 } 546 return status; 547 } 548 549 public OperationStatus getPrevNoDup(DatabaseEntry key, 550 DatabaseEntry pKey, 551 DatabaseEntry data, 552 LockMode lockMode) 553 throws DatabaseException { 554 555 OperationStatus status; 556 if (!initialized) { 557 return getLast(key, pKey, data, lockMode); 558 } 559 if (!range.hasBound()) { 560 setParams(key, pKey, data); 561 status = doGetPrevNoDup(lockMode); 562 endOperation(null, status, null, null, null); 563 return status; 564 } 565 if (range.singleKey) { 566 status = OperationStatus.NOTFOUND; 567 } else { 568 status = OperationStatus.NOTFOUND; 569 Cursor oldCursor = beginOperation(); 570 try { 571 status = doGetPrevNoDup(lockMode); 572 if (status == OperationStatus.SUCCESS && 573 !range.check(privKey)) { 574 status = OperationStatus.NOTFOUND; 575 } 576 } finally { 577 endOperation(oldCursor, status, key, pKey, data); 578 } 579 } 580 return status; 581 } 582 583 public OperationStatus getSearchKey(DatabaseEntry key, 584 DatabaseEntry pKey, 585 DatabaseEntry data, 586 LockMode lockMode) 587 throws DatabaseException { 588 589 OperationStatus status; 590 if (!range.hasBound()) { 591 setParams(key, pKey, data); 592 status = doGetSearchKey(lockMode); 593 endOperation(null, status, null, null, null); 594 return status; 595 } 596 if (!range.check(key)) { 597 status = OperationStatus.NOTFOUND; 598 } else if (pkRange != null) { 599 status = OperationStatus.NOTFOUND; 600 Cursor oldCursor = beginOperation(); 601 try { 602 shareData(key, privKey); 603 status = doGetSearchKey(lockMode); 604 if (status == OperationStatus.SUCCESS && 605 !pkRange.check(privPKey)) { 606 status = OperationStatus.NOTFOUND; 607 } 608 } finally { 609 endOperation(oldCursor, status, key, pKey, data); 610 } 611 } else { 612 shareData(key, privKey); 613 status = doGetSearchKey(lockMode); 614 endOperation(null, status, key, pKey, data); 615 } 616 return status; 617 } 618 619 public OperationStatus getSearchBoth(DatabaseEntry key, 620 DatabaseEntry pKey, 621 DatabaseEntry data, 622 LockMode lockMode) 623 throws DatabaseException { 624 625 OperationStatus status; 626 if (!range.hasBound()) { 627 setParams(key, pKey, data); 628 status = doGetSearchBoth(lockMode); 629 endOperation(null, status, null, null, null); 630 return status; 631 } 632 if (!range.check(key) || 633 (pkRange != null && !pkRange.check(pKey))) { 634 status = OperationStatus.NOTFOUND; 635 } else { 636 shareData(key, privKey); 637 if (secCursor != null) { 638 shareData(pKey, privPKey); 639 } else { 640 shareData(data, privData); 641 } 642 status = doGetSearchBoth(lockMode); 643 endOperation(null, status, key, pKey, data); 644 } 645 return status; 646 } 647 648 public OperationStatus getSearchKeyRange(DatabaseEntry key, 649 DatabaseEntry pKey, 650 DatabaseEntry data, 651 LockMode lockMode) 652 throws DatabaseException { 653 654 OperationStatus status = OperationStatus.NOTFOUND; 655 if (!range.hasBound()) { 656 setParams(key, pKey, data); 657 status = doGetSearchKeyRange(lockMode); 658 endOperation(null, status, null, null, null); 659 return status; 660 } 661 Cursor oldCursor = beginOperation(); 662 try { 663 shareData(key, privKey); 664 status = doGetSearchKeyRange(lockMode); 665 if (status == OperationStatus.SUCCESS && 666 (!range.check(privKey) || 667 (pkRange != null && !pkRange.check(pKey)))) { 668 status = OperationStatus.NOTFOUND; 669 } 670 } finally { 671 endOperation(oldCursor, status, key, pKey, data); 672 } 673 return status; 674 } 675 676 public OperationStatus getSearchBothRange(DatabaseEntry key, 677 DatabaseEntry pKey, 678 DatabaseEntry data, 679 LockMode lockMode) 680 throws DatabaseException { 681 682 OperationStatus status = OperationStatus.NOTFOUND; 683 if (!range.hasBound()) { 684 setParams(key, pKey, data); 685 status = doGetSearchBothRange(lockMode); 686 endOperation(null, status, null, null, null); 687 return status; 688 } 689 Cursor oldCursor = beginOperation(); 690 try { 691 shareData(key, privKey); 692 if (secCursor != null) { 693 shareData(pKey, privPKey); 694 } else { 695 shareData(data, privData); 696 } 697 status = doGetSearchBothRange(lockMode); 698 if (status == OperationStatus.SUCCESS && 699 (!range.check(privKey) || 700 (pkRange != null && !pkRange.check(pKey)))) { 701 status = OperationStatus.NOTFOUND; 702 } 703 } finally { 704 endOperation(oldCursor, status, key, pKey, data); 705 } 706 return status; 707 } 708 709 public OperationStatus getSearchRecordNumber(DatabaseEntry key, 710 DatabaseEntry pKey, 711 DatabaseEntry data, 712 LockMode lockMode) 713 throws DatabaseException { 714 715 OperationStatus status; 716 if (!range.hasBound()) { 717 setParams(key, pKey, data); 718 status = doGetSearchRecordNumber(lockMode); 719 endOperation(null, status, null, null, null); 720 return status; 721 } 722 if (!range.check(key)) { 723 status = OperationStatus.NOTFOUND; 724 } else { 725 shareData(key, privKey); 726 status = doGetSearchRecordNumber(lockMode); 727 endOperation(null, status, key, pKey, data); 728 } 729 return status; 730 } 731 732 public OperationStatus getNextDup(DatabaseEntry key, 733 DatabaseEntry pKey, 734 DatabaseEntry data, 735 LockMode lockMode) 736 throws DatabaseException { 737 738 if (!initialized) { 739 throw new DatabaseException("Cursor not initialized"); 740 } 741 OperationStatus status; 742 if (!range.hasBound()) { 743 setParams(key, pKey, data); 744 status = doGetNextDup(lockMode); 745 endOperation(null, status, null, null, null); 746 } else if (pkRange != null && pkRange.endKey != null) { 747 status = OperationStatus.NOTFOUND; 748 Cursor oldCursor = beginOperation(); 749 try { 750 status = doGetNextDup(lockMode); 751 if (status == OperationStatus.SUCCESS && 752 !pkRange.checkEnd(privPKey, true)) { 753 status = OperationStatus.NOTFOUND; 754 } 755 } finally { 756 endOperation(oldCursor, status, key, pKey, data); 757 } 758 } else { 759 status = doGetNextDup(lockMode); 760 endOperation(null, status, key, pKey, data); 761 } 762 return status; 763 } 764 765 public OperationStatus getPrevDup(DatabaseEntry key, 766 DatabaseEntry pKey, 767 DatabaseEntry data, 768 LockMode lockMode) 769 throws DatabaseException { 770 771 if (!initialized) { 772 throw new DatabaseException("Cursor not initialized"); 773 } 774 OperationStatus status; 775 if (!range.hasBound()) { 776 setParams(key, pKey, data); 777 status = doGetPrevDup(lockMode); 778 endOperation(null, status, null, null, null); 779 } else if (pkRange != null && pkRange.beginKey != null) { 780 status = OperationStatus.NOTFOUND; 781 Cursor oldCursor = beginOperation(); 782 try { 783 status = doGetPrevDup(lockMode); 784 if (status == OperationStatus.SUCCESS && 785 !pkRange.checkBegin(privPKey, true)) { 786 status = OperationStatus.NOTFOUND; 787 } 788 } finally { 789 endOperation(oldCursor, status, key, pKey, data); 790 } 791 } else { 792 status = doGetPrevDup(lockMode); 793 endOperation(null, status, key, pKey, data); 794 } 795 return status; 796 } 797 798 public OperationStatus getCurrent(DatabaseEntry key, 799 DatabaseEntry pKey, 800 DatabaseEntry data, 801 LockMode lockMode) 802 throws DatabaseException { 803 804 if (!initialized) { 805 throw new DatabaseException("Cursor not initialized"); 806 } 807 if (secCursor != null && pKey != null) { 808 return secCursor.getCurrent(key, pKey, data, lockMode); 809 } else { 810 return cursor.getCurrent(key, data, lockMode); 811 } 812 } 813 814 /* 815 * Pass-thru methods. 816 */ 817 818 public void close() 819 throws DatabaseException { 820 821 closeCursor(cursor); 822 } 823 824 public int count() 825 throws DatabaseException { 826 827 return cursor.count(); 828 } 829 830 public OperationStatus delete() 831 throws DatabaseException { 832 833 return cursor.delete(); 834 } 835 836 public OperationStatus put(DatabaseEntry key, DatabaseEntry data) 837 throws DatabaseException { 838 839 return cursor.put(key, data); 840 } 841 842 public OperationStatus putNoOverwrite(DatabaseEntry key, 843 DatabaseEntry data) 844 throws DatabaseException { 845 846 return cursor.putNoOverwrite(key, data); 847 } 848 849 public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data) 850 throws DatabaseException { 851 852 return cursor.putNoDupData(key, data); 853 } 854 855 public OperationStatus putCurrent(DatabaseEntry data) 856 throws DatabaseException { 857 858 return cursor.putCurrent(data); 859 } 860 861 public OperationStatus putAfter(DatabaseEntry key, DatabaseEntry data) 862 throws DatabaseException { 863 864 return DbCompat.putAfter(cursor, key, data); 865 } 866 867 public OperationStatus putBefore(DatabaseEntry key, DatabaseEntry data) 868 throws DatabaseException { 869 870 return DbCompat.putBefore(cursor, key, data); 871 } 872 873 private OperationStatus doGetFirst(LockMode lockMode) 874 throws DatabaseException { 875 876 if (secCursor != null && privPKey != null) { 877 return secCursor.getFirst(privKey, privPKey, privData, lockMode); 878 } else { 879 return cursor.getFirst(privKey, privData, lockMode); 880 } 881 } 882 883 private OperationStatus doGetLast(LockMode lockMode) 884 throws DatabaseException { 885 886 if (secCursor != null && privPKey != null) { 887 return secCursor.getLast(privKey, privPKey, privData, lockMode); 888 } else { 889 return cursor.getLast(privKey, privData, lockMode); 890 } 891 } 892 893 private OperationStatus doGetNext(LockMode lockMode) 894 throws DatabaseException { 895 896 if (secCursor != null && privPKey != null) { 897 return secCursor.getNext(privKey, privPKey, privData, lockMode); 898 } else { 899 return cursor.getNext(privKey, privData, lockMode); 900 } 901 } 902 903 private OperationStatus doGetNextDup(LockMode lockMode) 904 throws DatabaseException { 905 906 if (secCursor != null && privPKey != null) { 907 return secCursor.getNextDup(privKey, privPKey, privData, lockMode); 908 } else { 909 return cursor.getNextDup(privKey, privData, lockMode); 910 } 911 } 912 913 private OperationStatus doGetNextNoDup(LockMode lockMode) 914 throws DatabaseException { 915 916 if (secCursor != null && privPKey != null) { 917 return secCursor.getNextNoDup(privKey, privPKey, privData, 918 lockMode); 919 } else { 920 return cursor.getNextNoDup(privKey, privData, lockMode); 921 } 922 } 923 924 private OperationStatus doGetPrev(LockMode lockMode) 925 throws DatabaseException { 926 927 if (secCursor != null && privPKey != null) { 928 return secCursor.getPrev(privKey, privPKey, privData, lockMode); 929 } else { 930 return cursor.getPrev(privKey, privData, lockMode); 931 } 932 } 933 934 private OperationStatus doGetPrevDup(LockMode lockMode) 935 throws DatabaseException { 936 937 if (secCursor != null && privPKey != null) { 938 return secCursor.getPrevDup(privKey, privPKey, privData, lockMode); 939 } else { 940 return cursor.getPrevDup(privKey, privData, lockMode); 941 } 942 } 943 944 private OperationStatus doGetPrevNoDup(LockMode lockMode) 945 throws DatabaseException { 946 947 if (secCursor != null && privPKey != null) { 948 return secCursor.getPrevNoDup(privKey, privPKey, privData, 949 lockMode); 950 } else { 951 return cursor.getPrevNoDup(privKey, privData, lockMode); 952 } 953 } 954 955 private OperationStatus doGetSearchKey(LockMode lockMode) 956 throws DatabaseException { 957 958 if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) { 959 return OperationStatus.NOTFOUND; 960 } 961 if (secCursor != null && privPKey != null) { 962 return secCursor.getSearchKey(privKey, privPKey, privData, 963 lockMode); 964 } else { 965 return cursor.getSearchKey(privKey, privData, lockMode); 966 } 967 } 968 969 private OperationStatus doGetSearchKeyRange(LockMode lockMode) 970 throws DatabaseException { 971 972 if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) { 973 return OperationStatus.NOTFOUND; 974 } 975 if (secCursor != null && privPKey != null) { 976 return secCursor.getSearchKeyRange(privKey, privPKey, privData, 977 lockMode); 978 } else { 979 return cursor.getSearchKeyRange(privKey, privData, lockMode); 980 } 981 } 982 983 private OperationStatus doGetSearchBoth(LockMode lockMode) 984 throws DatabaseException { 985 986 if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) { 987 return OperationStatus.NOTFOUND; 988 } 989 if (secCursor != null && privPKey != null) { 990 return secCursor.getSearchBoth(privKey, privPKey, privData, 991 lockMode); 992 } else { 993 return cursor.getSearchBoth(privKey, privData, lockMode); 994 } 995 } 996 997 private OperationStatus doGetSearchBothRange(LockMode lockMode) 998 throws DatabaseException { 999 1000 if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) { 1001 return OperationStatus.NOTFOUND; 1002 } 1003 if (secCursor != null && privPKey != null) { 1004 return secCursor.getSearchBothRange(privKey, privPKey, 1005 privData, lockMode); 1006 } else { 1007 return cursor.getSearchBothRange(privKey, privData, lockMode); 1008 } 1009 } 1010 1011 private OperationStatus doGetSearchRecordNumber(LockMode lockMode) 1012 throws DatabaseException { 1013 1014 if (DbCompat.getRecordNumber(privKey) <= 0) { 1015 return OperationStatus.NOTFOUND; 1016 } 1017 if (secCursor != null && privPKey != null) { 1018 return DbCompat.getSearchRecordNumber(secCursor, privKey, privPKey, 1019 privData, lockMode); 1020 } else { 1021 return DbCompat.getSearchRecordNumber(cursor, privKey, privData, 1022 lockMode); 1023 } 1024 } 1025 1026 /* 1027 * Protected methods for duping and closing cursors. These are overridden 1028 * by the collections API to implement cursor pooling for CDS. 1029 */ 1030 1031 /** 1032 * Dups the given cursor. 1033 */ 1034 protected Cursor dupCursor(Cursor cursor, boolean samePosition) 1035 throws DatabaseException { 1036 1037 return cursor.dup(samePosition); 1038 } 1039 1040 /** 1041 * Closes the given cursor. 1042 */ 1043 protected void closeCursor(Cursor cursor) 1044 throws DatabaseException { 1045 1046 cursor.close(); 1047 } 1048 1049 /** 1050 * If the database is a RECNO or QUEUE database, we know its keys are 1051 * record numbers. We treat a non-positive record number as out of bounds, 1052 * that is, we return NOTFOUND rather than throwing 1053 * IllegalArgumentException as would happen if we passed a non-positive 1054 * record number into the DB cursor. This behavior is required by the 1055 * collections interface. 1056 */ 1057 protected boolean checkRecordNumber() { 1058 return false; 1059 } 1060} 1061