0001: /*-
0002: * See the file LICENSE for redistribution information.
0003: *
0004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
0005: *
0006: * $Id: RangeCursor.java,v 1.6.2.3 2008/01/07 15:14:21 cwl Exp $
0007: */
0008:
0009: package com.sleepycat.util.keyrange;
0010:
0011: import com.sleepycat.compat.DbCompat;
0012: import com.sleepycat.je.Cursor;
0013: import com.sleepycat.je.DatabaseEntry;
0014: import com.sleepycat.je.DatabaseException;
0015: import com.sleepycat.je.LockMode;
0016: import com.sleepycat.je.OperationStatus;
0017: import com.sleepycat.je.SecondaryCursor;
0018:
0019: /**
0020: * A cursor-like interface that enforces a key range. The method signatures
0021: * are actually those of SecondaryCursor, but the pKey parameter may be null.
0022: * It was done this way to avoid doubling the number of methods.
0023: *
0024: * <p>This is not a fully general implementation of a range cursor and should
0025: * not be used directly by applications; however, it may evolve into a
0026: * generally useful range cursor some day.</p>
0027: *
0028: * @author Mark Hayes
0029: */
0030: public class RangeCursor implements Cloneable {
0031:
0032: /**
0033: * The cursor and secondary cursor are the same object. The secCursor is
0034: * null if the database is not a secondary database.
0035: */
0036: private Cursor cursor;
0037: private SecondaryCursor secCursor;
0038:
0039: /**
0040: * The range is always non-null, but may be unbounded meaning that it is
0041: * open and not used.
0042: */
0043: private KeyRange range;
0044:
0045: /**
0046: * The pkRange may be non-null only if the range is a single-key range
0047: * and the cursor is a secondary cursor. It further restricts the range of
0048: * primary keys in a secondary database.
0049: */
0050: private KeyRange pkRange;
0051:
0052: /**
0053: * The privXxx entries are used only when the range is bounded. We read
0054: * into these private entries to avoid modifying the caller's entry
0055: * parameters in the case where we read successfully but the key is out of
0056: * range. In that case we return NOTFOUND and we want to leave the entry
0057: * parameters unchanged.
0058: */
0059: private DatabaseEntry privKey;
0060: private DatabaseEntry privPKey;
0061: private DatabaseEntry privData;
0062:
0063: /**
0064: * The initialized flag is set to true whenever we successfully position
0065: * the cursor. It is used to implement the getNext/Prev logic for doing a
0066: * getFirst/Last when the cursor is not initialized. We can't rely on
0067: * Cursor to do that for us, since if we position the underlying cursor
0068: * successfully but the key is out of range, we have no way to set the
0069: * underlying cursor to uninitialized. A range cursor always starts in the
0070: * uninitialized state.
0071: */
0072: private boolean initialized;
0073:
0074: /**
0075: * Creates a range cursor.
0076: */
0077: public RangeCursor(KeyRange range, Cursor cursor)
0078: throws DatabaseException {
0079:
0080: this .range = range;
0081: this .cursor = cursor;
0082: init();
0083: }
0084:
0085: /**
0086: * Creates a range cursor with a duplicate range.
0087: */
0088: public RangeCursor(KeyRange range, KeyRange pkRange, Cursor cursor)
0089: throws DatabaseException {
0090:
0091: if (pkRange != null && !range.singleKey) {
0092: throw new IllegalArgumentException();
0093: }
0094: this .range = range;
0095: this .pkRange = pkRange;
0096: this .cursor = cursor;
0097: init();
0098: if (pkRange != null && secCursor == null) {
0099: throw new IllegalArgumentException();
0100: }
0101: }
0102:
0103: /**
0104: * Create a cloned range cursor. The caller must clone the underlying
0105: * cursor before using this constructor, because cursor open/close is
0106: * handled specially for CDS cursors outside this class.
0107: */
0108: public RangeCursor dup(boolean samePosition)
0109: throws DatabaseException {
0110:
0111: try {
0112: RangeCursor c = (RangeCursor) super .clone();
0113: c.cursor = dupCursor(cursor, samePosition);
0114: c.init();
0115: return c;
0116: } catch (CloneNotSupportedException neverHappens) {
0117: return null;
0118: }
0119: }
0120:
0121: /**
0122: * Used for opening and duping (cloning).
0123: */
0124: private void init() {
0125:
0126: if (cursor instanceof SecondaryCursor) {
0127: secCursor = (SecondaryCursor) cursor;
0128: } else {
0129: secCursor = null;
0130: }
0131:
0132: if (range.hasBound()) {
0133: privKey = new DatabaseEntry();
0134: privPKey = new DatabaseEntry();
0135: privData = new DatabaseEntry();
0136: } else {
0137: privKey = null;
0138: privPKey = null;
0139: privData = null;
0140: }
0141: }
0142:
0143: /**
0144: * Returns whether the cursor is initialized at a valid position.
0145: */
0146: public boolean isInitialized() {
0147: return initialized;
0148: }
0149:
0150: /**
0151: * Returns the underlying cursor. Used for cloning.
0152: */
0153: public Cursor getCursor() {
0154: return cursor;
0155: }
0156:
0157: /**
0158: * When an unbounded range is used, this method is called to use the
0159: * callers entry parameters directly, to avoid the extra step of copying
0160: * between the private entries and the caller's entries.
0161: */
0162: private void setParams(DatabaseEntry key, DatabaseEntry pKey,
0163: DatabaseEntry data) {
0164: privKey = key;
0165: privPKey = pKey;
0166: privData = data;
0167: }
0168:
0169: /**
0170: * Dups the cursor, sets the cursor and secCursor fields to the duped
0171: * cursor, and returns the old cursor. Always call endOperation in a
0172: * finally clause after calling beginOperation.
0173: *
0174: * <p>If the returned cursor == the cursor field, the cursor is
0175: * uninitialized and was not duped; this case is handled correctly by
0176: * endOperation.</p>
0177: */
0178: private Cursor beginOperation() throws DatabaseException {
0179:
0180: Cursor oldCursor = cursor;
0181: if (initialized) {
0182: cursor = dupCursor(cursor, true);
0183: if (secCursor != null) {
0184: secCursor = (SecondaryCursor) cursor;
0185: }
0186: } else {
0187: return cursor;
0188: }
0189: return oldCursor;
0190: }
0191:
0192: /**
0193: * If the operation succeded, leaves the duped cursor in place and closes
0194: * the oldCursor. If the operation failed, moves the oldCursor back in
0195: * place and closes the duped cursor. oldCursor may be null if
0196: * beginOperation was not called, in cases where we don't need to dup
0197: * the cursor. Always call endOperation when a successful operation ends,
0198: * in order to set the initialized field.
0199: */
0200: private void endOperation(Cursor oldCursor, OperationStatus status,
0201: DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data)
0202: throws DatabaseException {
0203:
0204: if (status == OperationStatus.SUCCESS) {
0205: if (oldCursor != null && oldCursor != cursor) {
0206: closeCursor(oldCursor);
0207: }
0208: if (key != null) {
0209: swapData(key, privKey);
0210: }
0211: if (pKey != null && secCursor != null) {
0212: swapData(pKey, privPKey);
0213: }
0214: if (data != null) {
0215: swapData(data, privData);
0216: }
0217: initialized = true;
0218: } else {
0219: if (oldCursor != null && oldCursor != cursor) {
0220: closeCursor(cursor);
0221: cursor = oldCursor;
0222: if (secCursor != null) {
0223: secCursor = (SecondaryCursor) cursor;
0224: }
0225: }
0226: }
0227: }
0228:
0229: /**
0230: * Swaps the contents of the two entries. Used to return entry data to
0231: * the caller when the operation was successful.
0232: */
0233: private static void swapData(DatabaseEntry e1, DatabaseEntry e2) {
0234:
0235: byte[] d1 = e1.getData();
0236: int o1 = e1.getOffset();
0237: int s1 = e1.getSize();
0238:
0239: e1.setData(e2.getData(), e2.getOffset(), e2.getSize());
0240: e2.setData(d1, o1, s1);
0241: }
0242:
0243: /**
0244: * Shares the same byte array, offset and size between two entries.
0245: * Used when copying the entry data is not necessary because it is known
0246: * that the underlying operation will not modify the entry, for example,
0247: * with getSearchKey.
0248: */
0249: private static void shareData(DatabaseEntry from, DatabaseEntry to) {
0250:
0251: if (from != null) {
0252: to
0253: .setData(from.getData(), from.getOffset(), from
0254: .getSize());
0255: }
0256: }
0257:
0258: public OperationStatus getFirst(DatabaseEntry key,
0259: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0260: throws DatabaseException {
0261:
0262: OperationStatus status;
0263: if (!range.hasBound()) {
0264: setParams(key, pKey, data);
0265: status = doGetFirst(lockMode);
0266: endOperation(null, status, null, null, null);
0267: return status;
0268: }
0269: if (pkRange != null) {
0270: KeyRange.copy(range.beginKey, privKey);
0271: if (pkRange.singleKey) {
0272: KeyRange.copy(pkRange.beginKey, privPKey);
0273: status = doGetSearchBoth(lockMode);
0274: endOperation(null, status, key, pKey, data);
0275: } else {
0276: status = OperationStatus.NOTFOUND;
0277: Cursor oldCursor = beginOperation();
0278: try {
0279: if (pkRange.beginKey == null) {
0280: status = doGetSearchKey(lockMode);
0281: } else {
0282: KeyRange.copy(pkRange.beginKey, privPKey);
0283: status = doGetSearchBothRange(lockMode);
0284: if (status == OperationStatus.SUCCESS
0285: && !pkRange.beginInclusive
0286: && pkRange.compare(privPKey,
0287: pkRange.beginKey) == 0) {
0288: status = doGetNextDup(lockMode);
0289: }
0290: }
0291: if (status == OperationStatus.SUCCESS
0292: && !pkRange.check(privPKey)) {
0293: status = OperationStatus.NOTFOUND;
0294: }
0295: } finally {
0296: endOperation(oldCursor, status, key, pKey, data);
0297: }
0298: }
0299: } else if (range.singleKey) {
0300: KeyRange.copy(range.beginKey, privKey);
0301: status = doGetSearchKey(lockMode);
0302: endOperation(null, status, key, pKey, data);
0303: } else {
0304: status = OperationStatus.NOTFOUND;
0305: Cursor oldCursor = beginOperation();
0306: try {
0307: if (range.beginKey == null) {
0308: status = doGetFirst(lockMode);
0309: } else {
0310: KeyRange.copy(range.beginKey, privKey);
0311: status = doGetSearchKeyRange(lockMode);
0312: if (status == OperationStatus.SUCCESS
0313: && !range.beginInclusive
0314: && range.compare(privKey, range.beginKey) == 0) {
0315: status = doGetNextNoDup(lockMode);
0316: }
0317: }
0318: if (status == OperationStatus.SUCCESS
0319: && !range.check(privKey)) {
0320: status = OperationStatus.NOTFOUND;
0321: }
0322: } finally {
0323: endOperation(oldCursor, status, key, pKey, data);
0324: }
0325: }
0326: return status;
0327: }
0328:
0329: public OperationStatus getLast(DatabaseEntry key,
0330: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0331: throws DatabaseException {
0332:
0333: OperationStatus status = OperationStatus.NOTFOUND;
0334: if (!range.hasBound()) {
0335: setParams(key, pKey, data);
0336: status = doGetLast(lockMode);
0337: endOperation(null, status, null, null, null);
0338: return status;
0339: }
0340: Cursor oldCursor = beginOperation();
0341: try {
0342: if (pkRange != null) {
0343: KeyRange.copy(range.beginKey, privKey);
0344: boolean doLast = false;
0345: if (pkRange.endKey == null) {
0346: doLast = true;
0347: } else {
0348: KeyRange.copy(pkRange.endKey, privPKey);
0349: status = doGetSearchBothRange(lockMode);
0350: if (status == OperationStatus.SUCCESS) {
0351: if (!pkRange.endInclusive
0352: || pkRange.compare(pkRange.endKey,
0353: privPKey) != 0) {
0354: status = doGetPrevDup(lockMode);
0355: }
0356: } else {
0357: KeyRange.copy(range.beginKey, privKey);
0358: doLast = true;
0359: }
0360: }
0361: if (doLast) {
0362: status = doGetSearchKey(lockMode);
0363: if (status == OperationStatus.SUCCESS) {
0364: status = doGetNextNoDup(lockMode);
0365: if (status == OperationStatus.SUCCESS) {
0366: status = doGetPrev(lockMode);
0367: } else {
0368: status = doGetLast(lockMode);
0369: }
0370: }
0371: }
0372: if (status == OperationStatus.SUCCESS
0373: && !pkRange.check(privPKey)) {
0374: status = OperationStatus.NOTFOUND;
0375: }
0376: } else if (range.endKey == null) {
0377: status = doGetLast(lockMode);
0378: } else {
0379: KeyRange.copy(range.endKey, privKey);
0380: status = doGetSearchKeyRange(lockMode);
0381: if (status == OperationStatus.SUCCESS) {
0382: if (range.endInclusive
0383: && range.compare(range.endKey, privKey) == 0) {
0384: /* Skip this step if dups are not configured? */
0385: status = doGetNextNoDup(lockMode);
0386: if (status == OperationStatus.SUCCESS) {
0387: status = doGetPrev(lockMode);
0388: } else {
0389: status = doGetLast(lockMode);
0390: }
0391: } else {
0392: status = doGetPrev(lockMode);
0393: }
0394: } else {
0395: status = doGetLast(lockMode);
0396: }
0397: }
0398: if (status == OperationStatus.SUCCESS
0399: && !range.checkBegin(privKey, true)) {
0400: status = OperationStatus.NOTFOUND;
0401: }
0402: } finally {
0403: endOperation(oldCursor, status, key, pKey, data);
0404: }
0405: return status;
0406: }
0407:
0408: public OperationStatus getNext(DatabaseEntry key,
0409: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0410: throws DatabaseException {
0411:
0412: OperationStatus status;
0413: if (!initialized) {
0414: return getFirst(key, pKey, data, lockMode);
0415: }
0416: if (!range.hasBound()) {
0417: setParams(key, pKey, data);
0418: status = doGetNext(lockMode);
0419: endOperation(null, status, null, null, null);
0420: return status;
0421: }
0422: if (pkRange != null) {
0423: if (pkRange.endKey == null) {
0424: status = doGetNextDup(lockMode);
0425: endOperation(null, status, key, pKey, data);
0426: } else {
0427: status = OperationStatus.NOTFOUND;
0428: Cursor oldCursor = beginOperation();
0429: try {
0430: status = doGetNextDup(lockMode);
0431: if (status == OperationStatus.SUCCESS
0432: && !pkRange.checkEnd(privPKey, true)) {
0433: status = OperationStatus.NOTFOUND;
0434: }
0435: } finally {
0436: endOperation(oldCursor, status, key, pKey, data);
0437: }
0438: }
0439: } else if (range.singleKey) {
0440: status = doGetNextDup(lockMode);
0441: endOperation(null, status, key, pKey, data);
0442: } else {
0443: status = OperationStatus.NOTFOUND;
0444: Cursor oldCursor = beginOperation();
0445: try {
0446: status = doGetNext(lockMode);
0447: if (status == OperationStatus.SUCCESS
0448: && !range.check(privKey)) {
0449: status = OperationStatus.NOTFOUND;
0450: }
0451: } finally {
0452: endOperation(oldCursor, status, key, pKey, data);
0453: }
0454: }
0455: return status;
0456: }
0457:
0458: public OperationStatus getNextNoDup(DatabaseEntry key,
0459: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0460: throws DatabaseException {
0461:
0462: OperationStatus status;
0463: if (!initialized) {
0464: return getFirst(key, pKey, data, lockMode);
0465: }
0466: if (!range.hasBound()) {
0467: setParams(key, pKey, data);
0468: status = doGetNextNoDup(lockMode);
0469: endOperation(null, status, null, null, null);
0470: return status;
0471: }
0472: if (range.singleKey) {
0473: status = OperationStatus.NOTFOUND;
0474: } else {
0475: status = OperationStatus.NOTFOUND;
0476: Cursor oldCursor = beginOperation();
0477: try {
0478: status = doGetNextNoDup(lockMode);
0479: if (status == OperationStatus.SUCCESS
0480: && !range.check(privKey)) {
0481: status = OperationStatus.NOTFOUND;
0482: }
0483: } finally {
0484: endOperation(oldCursor, status, key, pKey, data);
0485: }
0486: }
0487: return status;
0488: }
0489:
0490: public OperationStatus getPrev(DatabaseEntry key,
0491: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0492: throws DatabaseException {
0493:
0494: OperationStatus status;
0495: if (!initialized) {
0496: return getLast(key, pKey, data, lockMode);
0497: }
0498: if (!range.hasBound()) {
0499: setParams(key, pKey, data);
0500: status = doGetPrev(lockMode);
0501: endOperation(null, status, null, null, null);
0502: return status;
0503: }
0504: if (pkRange != null) {
0505: if (pkRange.beginKey == null) {
0506: status = doGetPrevDup(lockMode);
0507: endOperation(null, status, key, pKey, data);
0508: } else {
0509: status = OperationStatus.NOTFOUND;
0510: Cursor oldCursor = beginOperation();
0511: try {
0512: status = doGetPrevDup(lockMode);
0513: if (status == OperationStatus.SUCCESS
0514: && !pkRange.checkBegin(privPKey, true)) {
0515: status = OperationStatus.NOTFOUND;
0516: }
0517: } finally {
0518: endOperation(oldCursor, status, key, pKey, data);
0519: }
0520: }
0521: } else if (range.singleKey) {
0522: status = doGetPrevDup(lockMode);
0523: endOperation(null, status, key, pKey, data);
0524: } else {
0525: status = OperationStatus.NOTFOUND;
0526: Cursor oldCursor = beginOperation();
0527: try {
0528: status = doGetPrev(lockMode);
0529: if (status == OperationStatus.SUCCESS
0530: && !range.check(privKey)) {
0531: status = OperationStatus.NOTFOUND;
0532: }
0533: } finally {
0534: endOperation(oldCursor, status, key, pKey, data);
0535: }
0536: }
0537: return status;
0538: }
0539:
0540: public OperationStatus getPrevNoDup(DatabaseEntry key,
0541: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0542: throws DatabaseException {
0543:
0544: OperationStatus status;
0545: if (!initialized) {
0546: return getLast(key, pKey, data, lockMode);
0547: }
0548: if (!range.hasBound()) {
0549: setParams(key, pKey, data);
0550: status = doGetPrevNoDup(lockMode);
0551: endOperation(null, status, null, null, null);
0552: return status;
0553: }
0554: if (range.singleKey) {
0555: status = OperationStatus.NOTFOUND;
0556: } else {
0557: status = OperationStatus.NOTFOUND;
0558: Cursor oldCursor = beginOperation();
0559: try {
0560: status = doGetPrevNoDup(lockMode);
0561: if (status == OperationStatus.SUCCESS
0562: && !range.check(privKey)) {
0563: status = OperationStatus.NOTFOUND;
0564: }
0565: } finally {
0566: endOperation(oldCursor, status, key, pKey, data);
0567: }
0568: }
0569: return status;
0570: }
0571:
0572: public OperationStatus getSearchKey(DatabaseEntry key,
0573: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0574: throws DatabaseException {
0575:
0576: OperationStatus status;
0577: if (!range.hasBound()) {
0578: setParams(key, pKey, data);
0579: status = doGetSearchKey(lockMode);
0580: endOperation(null, status, null, null, null);
0581: return status;
0582: }
0583: if (!range.check(key)) {
0584: status = OperationStatus.NOTFOUND;
0585: } else if (pkRange != null) {
0586: status = OperationStatus.NOTFOUND;
0587: Cursor oldCursor = beginOperation();
0588: try {
0589: shareData(key, privKey);
0590: status = doGetSearchKey(lockMode);
0591: if (status == OperationStatus.SUCCESS
0592: && !pkRange.check(privPKey)) {
0593: status = OperationStatus.NOTFOUND;
0594: }
0595: } finally {
0596: endOperation(oldCursor, status, key, pKey, data);
0597: }
0598: } else {
0599: shareData(key, privKey);
0600: status = doGetSearchKey(lockMode);
0601: endOperation(null, status, key, pKey, data);
0602: }
0603: return status;
0604: }
0605:
0606: public OperationStatus getSearchBoth(DatabaseEntry key,
0607: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0608: throws DatabaseException {
0609:
0610: OperationStatus status;
0611: if (!range.hasBound()) {
0612: setParams(key, pKey, data);
0613: status = doGetSearchBoth(lockMode);
0614: endOperation(null, status, null, null, null);
0615: return status;
0616: }
0617: if (!range.check(key)
0618: || (pkRange != null && !pkRange.check(pKey))) {
0619: status = OperationStatus.NOTFOUND;
0620: } else {
0621: shareData(key, privKey);
0622: if (secCursor != null) {
0623: shareData(pKey, privPKey);
0624: } else {
0625: shareData(data, privData);
0626: }
0627: status = doGetSearchBoth(lockMode);
0628: endOperation(null, status, key, pKey, data);
0629: }
0630: return status;
0631: }
0632:
0633: public OperationStatus getSearchKeyRange(DatabaseEntry key,
0634: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0635: throws DatabaseException {
0636:
0637: OperationStatus status = OperationStatus.NOTFOUND;
0638: if (!range.hasBound()) {
0639: setParams(key, pKey, data);
0640: status = doGetSearchKeyRange(lockMode);
0641: endOperation(null, status, null, null, null);
0642: return status;
0643: }
0644: Cursor oldCursor = beginOperation();
0645: try {
0646: shareData(key, privKey);
0647: status = doGetSearchKeyRange(lockMode);
0648: if (status == OperationStatus.SUCCESS
0649: && (!range.check(privKey) || (pkRange != null && !pkRange
0650: .check(pKey)))) {
0651: status = OperationStatus.NOTFOUND;
0652: }
0653: } finally {
0654: endOperation(oldCursor, status, key, pKey, data);
0655: }
0656: return status;
0657: }
0658:
0659: public OperationStatus getSearchBothRange(DatabaseEntry key,
0660: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0661: throws DatabaseException {
0662:
0663: OperationStatus status = OperationStatus.NOTFOUND;
0664: if (!range.hasBound()) {
0665: setParams(key, pKey, data);
0666: status = doGetSearchBothRange(lockMode);
0667: endOperation(null, status, null, null, null);
0668: return status;
0669: }
0670: Cursor oldCursor = beginOperation();
0671: try {
0672: shareData(key, privKey);
0673: if (secCursor != null) {
0674: shareData(pKey, privPKey);
0675: } else {
0676: shareData(data, privData);
0677: }
0678: status = doGetSearchBothRange(lockMode);
0679: if (status == OperationStatus.SUCCESS
0680: && (!range.check(privKey) || (pkRange != null && !pkRange
0681: .check(pKey)))) {
0682: status = OperationStatus.NOTFOUND;
0683: }
0684: } finally {
0685: endOperation(oldCursor, status, key, pKey, data);
0686: }
0687: return status;
0688: }
0689:
0690: public OperationStatus getSearchRecordNumber(DatabaseEntry key,
0691: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0692: throws DatabaseException {
0693:
0694: OperationStatus status;
0695: if (!range.hasBound()) {
0696: setParams(key, pKey, data);
0697: status = doGetSearchRecordNumber(lockMode);
0698: endOperation(null, status, null, null, null);
0699: return status;
0700: }
0701: if (!range.check(key)) {
0702: status = OperationStatus.NOTFOUND;
0703: } else {
0704: shareData(key, privKey);
0705: status = doGetSearchRecordNumber(lockMode);
0706: endOperation(null, status, key, pKey, data);
0707: }
0708: return status;
0709: }
0710:
0711: public OperationStatus getNextDup(DatabaseEntry key,
0712: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0713: throws DatabaseException {
0714:
0715: if (!initialized) {
0716: throw new DatabaseException("Cursor not initialized");
0717: }
0718: OperationStatus status;
0719: if (!range.hasBound()) {
0720: setParams(key, pKey, data);
0721: status = doGetNextDup(lockMode);
0722: endOperation(null, status, null, null, null);
0723: } else if (pkRange != null && pkRange.endKey != null) {
0724: status = OperationStatus.NOTFOUND;
0725: Cursor oldCursor = beginOperation();
0726: try {
0727: status = doGetNextDup(lockMode);
0728: if (status == OperationStatus.SUCCESS
0729: && !pkRange.checkEnd(privPKey, true)) {
0730: status = OperationStatus.NOTFOUND;
0731: }
0732: } finally {
0733: endOperation(oldCursor, status, key, pKey, data);
0734: }
0735: } else {
0736: status = doGetNextDup(lockMode);
0737: endOperation(null, status, key, pKey, data);
0738: }
0739: return status;
0740: }
0741:
0742: public OperationStatus getPrevDup(DatabaseEntry key,
0743: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0744: throws DatabaseException {
0745:
0746: if (!initialized) {
0747: throw new DatabaseException("Cursor not initialized");
0748: }
0749: OperationStatus status;
0750: if (!range.hasBound()) {
0751: setParams(key, pKey, data);
0752: status = doGetPrevDup(lockMode);
0753: endOperation(null, status, null, null, null);
0754: } else if (pkRange != null && pkRange.beginKey != null) {
0755: status = OperationStatus.NOTFOUND;
0756: Cursor oldCursor = beginOperation();
0757: try {
0758: status = doGetPrevDup(lockMode);
0759: if (status == OperationStatus.SUCCESS
0760: && !pkRange.checkBegin(privPKey, true)) {
0761: status = OperationStatus.NOTFOUND;
0762: }
0763: } finally {
0764: endOperation(oldCursor, status, key, pKey, data);
0765: }
0766: } else {
0767: status = doGetPrevDup(lockMode);
0768: endOperation(null, status, key, pKey, data);
0769: }
0770: return status;
0771: }
0772:
0773: public OperationStatus getCurrent(DatabaseEntry key,
0774: DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)
0775: throws DatabaseException {
0776:
0777: if (!initialized) {
0778: throw new DatabaseException("Cursor not initialized");
0779: }
0780: if (secCursor != null && pKey != null) {
0781: return secCursor.getCurrent(key, pKey, data, lockMode);
0782: } else {
0783: return cursor.getCurrent(key, data, lockMode);
0784: }
0785: }
0786:
0787: /*
0788: * Pass-thru methods.
0789: */
0790:
0791: public void close() throws DatabaseException {
0792:
0793: closeCursor(cursor);
0794: }
0795:
0796: public int count() throws DatabaseException {
0797:
0798: return cursor.count();
0799: }
0800:
0801: public OperationStatus delete() throws DatabaseException {
0802:
0803: return cursor.delete();
0804: }
0805:
0806: public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
0807: throws DatabaseException {
0808:
0809: return cursor.put(key, data);
0810: }
0811:
0812: public OperationStatus putNoOverwrite(DatabaseEntry key,
0813: DatabaseEntry data) throws DatabaseException {
0814:
0815: return cursor.putNoOverwrite(key, data);
0816: }
0817:
0818: public OperationStatus putNoDupData(DatabaseEntry key,
0819: DatabaseEntry data) throws DatabaseException {
0820:
0821: return cursor.putNoDupData(key, data);
0822: }
0823:
0824: public OperationStatus putCurrent(DatabaseEntry data)
0825: throws DatabaseException {
0826:
0827: return cursor.putCurrent(data);
0828: }
0829:
0830: public OperationStatus putAfter(DatabaseEntry key,
0831: DatabaseEntry data) throws DatabaseException {
0832:
0833: return DbCompat.putAfter(cursor, key, data);
0834: }
0835:
0836: public OperationStatus putBefore(DatabaseEntry key,
0837: DatabaseEntry data) throws DatabaseException {
0838:
0839: return DbCompat.putBefore(cursor, key, data);
0840: }
0841:
0842: private OperationStatus doGetFirst(LockMode lockMode)
0843: throws DatabaseException {
0844:
0845: if (secCursor != null && privPKey != null) {
0846: return secCursor.getFirst(privKey, privPKey, privData,
0847: lockMode);
0848: } else {
0849: return cursor.getFirst(privKey, privData, lockMode);
0850: }
0851: }
0852:
0853: private OperationStatus doGetLast(LockMode lockMode)
0854: throws DatabaseException {
0855:
0856: if (secCursor != null && privPKey != null) {
0857: return secCursor.getLast(privKey, privPKey, privData,
0858: lockMode);
0859: } else {
0860: return cursor.getLast(privKey, privData, lockMode);
0861: }
0862: }
0863:
0864: private OperationStatus doGetNext(LockMode lockMode)
0865: throws DatabaseException {
0866:
0867: if (secCursor != null && privPKey != null) {
0868: return secCursor.getNext(privKey, privPKey, privData,
0869: lockMode);
0870: } else {
0871: return cursor.getNext(privKey, privData, lockMode);
0872: }
0873: }
0874:
0875: private OperationStatus doGetNextDup(LockMode lockMode)
0876: throws DatabaseException {
0877:
0878: if (secCursor != null && privPKey != null) {
0879: return secCursor.getNextDup(privKey, privPKey, privData,
0880: lockMode);
0881: } else {
0882: return cursor.getNextDup(privKey, privData, lockMode);
0883: }
0884: }
0885:
0886: private OperationStatus doGetNextNoDup(LockMode lockMode)
0887: throws DatabaseException {
0888:
0889: if (secCursor != null && privPKey != null) {
0890: return secCursor.getNextNoDup(privKey, privPKey, privData,
0891: lockMode);
0892: } else {
0893: return cursor.getNextNoDup(privKey, privData, lockMode);
0894: }
0895: }
0896:
0897: private OperationStatus doGetPrev(LockMode lockMode)
0898: throws DatabaseException {
0899:
0900: if (secCursor != null && privPKey != null) {
0901: return secCursor.getPrev(privKey, privPKey, privData,
0902: lockMode);
0903: } else {
0904: return cursor.getPrev(privKey, privData, lockMode);
0905: }
0906: }
0907:
0908: private OperationStatus doGetPrevDup(LockMode lockMode)
0909: throws DatabaseException {
0910:
0911: if (secCursor != null && privPKey != null) {
0912: return secCursor.getPrevDup(privKey, privPKey, privData,
0913: lockMode);
0914: } else {
0915: return cursor.getPrevDup(privKey, privData, lockMode);
0916: }
0917: }
0918:
0919: private OperationStatus doGetPrevNoDup(LockMode lockMode)
0920: throws DatabaseException {
0921:
0922: if (secCursor != null && privPKey != null) {
0923: return secCursor.getPrevNoDup(privKey, privPKey, privData,
0924: lockMode);
0925: } else {
0926: return cursor.getPrevNoDup(privKey, privData, lockMode);
0927: }
0928: }
0929:
0930: private OperationStatus doGetSearchKey(LockMode lockMode)
0931: throws DatabaseException {
0932:
0933: if (checkRecordNumber()
0934: && DbCompat.getRecordNumber(privKey) <= 0) {
0935: return OperationStatus.NOTFOUND;
0936: }
0937: if (secCursor != null && privPKey != null) {
0938: return secCursor.getSearchKey(privKey, privPKey, privData,
0939: lockMode);
0940: } else {
0941: return cursor.getSearchKey(privKey, privData, lockMode);
0942: }
0943: }
0944:
0945: private OperationStatus doGetSearchKeyRange(LockMode lockMode)
0946: throws DatabaseException {
0947:
0948: if (checkRecordNumber()
0949: && DbCompat.getRecordNumber(privKey) <= 0) {
0950: return OperationStatus.NOTFOUND;
0951: }
0952: if (secCursor != null && privPKey != null) {
0953: return secCursor.getSearchKeyRange(privKey, privPKey,
0954: privData, lockMode);
0955: } else {
0956: return cursor
0957: .getSearchKeyRange(privKey, privData, lockMode);
0958: }
0959: }
0960:
0961: private OperationStatus doGetSearchBoth(LockMode lockMode)
0962: throws DatabaseException {
0963:
0964: if (checkRecordNumber()
0965: && DbCompat.getRecordNumber(privKey) <= 0) {
0966: return OperationStatus.NOTFOUND;
0967: }
0968: if (secCursor != null && privPKey != null) {
0969: return secCursor.getSearchBoth(privKey, privPKey, privData,
0970: lockMode);
0971: } else {
0972: return cursor.getSearchBoth(privKey, privData, lockMode);
0973: }
0974: }
0975:
0976: private OperationStatus doGetSearchBothRange(LockMode lockMode)
0977: throws DatabaseException {
0978:
0979: if (checkRecordNumber()
0980: && DbCompat.getRecordNumber(privKey) <= 0) {
0981: return OperationStatus.NOTFOUND;
0982: }
0983: if (secCursor != null && privPKey != null) {
0984: return secCursor.getSearchBothRange(privKey, privPKey,
0985: privData, lockMode);
0986: } else {
0987: return cursor.getSearchBothRange(privKey, privData,
0988: lockMode);
0989: }
0990: }
0991:
0992: private OperationStatus doGetSearchRecordNumber(LockMode lockMode)
0993: throws DatabaseException {
0994:
0995: if (DbCompat.getRecordNumber(privKey) <= 0) {
0996: return OperationStatus.NOTFOUND;
0997: }
0998: if (secCursor != null && privPKey != null) {
0999: return DbCompat.getSearchRecordNumber(secCursor, privKey,
1000: privPKey, privData, lockMode);
1001: } else {
1002: return DbCompat.getSearchRecordNumber(cursor, privKey,
1003: privData, lockMode);
1004: }
1005: }
1006:
1007: /*
1008: * Protected methods for duping and closing cursors. These are overridden
1009: * by the collections API to implement cursor pooling for CDS.
1010: */
1011:
1012: /**
1013: * Dups the given cursor.
1014: */
1015: protected Cursor dupCursor(Cursor cursor, boolean samePosition)
1016: throws DatabaseException {
1017:
1018: return cursor.dup(samePosition);
1019: }
1020:
1021: /**
1022: * Closes the given cursor.
1023: */
1024: protected void closeCursor(Cursor cursor) throws DatabaseException {
1025:
1026: cursor.close();
1027: }
1028:
1029: /**
1030: * If the database is a RECNO or QUEUE database, we know its keys are
1031: * record numbers. We treat a non-positive record number as out of bounds,
1032: * that is, we return NOTFOUND rather than throwing
1033: * IllegalArgumentException as would happen if we passed a non-positive
1034: * record number into the DB cursor. This behavior is required by the
1035: * collections interface.
1036: */
1037: protected boolean checkRecordNumber() {
1038: return false;
1039: }
1040: }
|