package com.sleepycat.util.keyrange;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.db.Cursor;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.db.SecondaryCursor;
public class RangeCursor implements Cloneable {
private Cursor cursor;
private SecondaryCursor secCursor;
private KeyRange range;
private KeyRange pkRange;
private boolean sortedDups;
private DatabaseEntry privKey;
private DatabaseEntry privPKey;
private DatabaseEntry privData;
private boolean initialized;
public RangeCursor(KeyRange range,
KeyRange pkRange,
boolean sortedDups,
Cursor cursor)
throws DatabaseException {
if (pkRange != null && !range.singleKey) {
throw new IllegalArgumentException();
}
this.range = range;
this.pkRange = pkRange;
this.sortedDups = sortedDups;
this.cursor = cursor;
init();
if (pkRange != null && secCursor == null) {
throw new IllegalArgumentException();
}
}
public RangeCursor dup(boolean samePosition)
throws DatabaseException {
try {
RangeCursor c = (RangeCursor) super.clone();
c.cursor = dupCursor(cursor, samePosition);
c.init();
return c;
} catch (CloneNotSupportedException neverHappens) {
return null;
}
}
private void init() {
if (cursor instanceof SecondaryCursor) {
secCursor = (SecondaryCursor) cursor;
} else {
secCursor = null;
}
if (range.hasBound()) {
privKey = new DatabaseEntry();
privPKey = new DatabaseEntry();
privData = new DatabaseEntry();
} else {
privKey = null;
privPKey = null;
privData = null;
}
}
public boolean isInitialized() {
return initialized;
}
public Cursor getCursor() {
return cursor;
}
private void setParams(DatabaseEntry key, DatabaseEntry pKey,
DatabaseEntry data) {
privKey = key;
privPKey = pKey;
privData = data;
}
private Cursor beginOperation()
throws DatabaseException {
Cursor oldCursor = cursor;
if (initialized) {
cursor = dupCursor(cursor, true);
if (secCursor != null) {
secCursor = (SecondaryCursor) cursor;
}
} else {
return cursor;
}
return oldCursor;
}
private void endOperation(Cursor oldCursor, OperationStatus status,
DatabaseEntry key, DatabaseEntry pKey,
DatabaseEntry data)
throws DatabaseException {
if (status == OperationStatus.SUCCESS) {
if (oldCursor != null && oldCursor != cursor) {
closeCursor(oldCursor);
}
if (key != null) {
swapData(key, privKey);
}
if (pKey != null && secCursor != null) {
swapData(pKey, privPKey);
}
if (data != null) {
swapData(data, privData);
}
initialized = true;
} else {
if (oldCursor != null && oldCursor != cursor) {
closeCursor(cursor);
cursor = oldCursor;
if (secCursor != null) {
secCursor = (SecondaryCursor) cursor;
}
}
}
}
private static void swapData(DatabaseEntry e1, DatabaseEntry e2) {
byte[] d1 = e1.getData();
int o1 = e1.getOffset();
int s1 = e1.getSize();
e1.setData(e2.getData(), e2.getOffset(), e2.getSize());
e2.setData(d1, o1, s1);
}
private static void shareData(DatabaseEntry from, DatabaseEntry to) {
if (from != null) {
to.setData(from.getData(), from.getOffset(), from.getSize());
}
}
public OperationStatus getFirst(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetFirst(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (pkRange != null) {
KeyRange.copy(range.beginKey, privKey);
if (pkRange.singleKey) {
KeyRange.copy(pkRange.beginKey, privPKey);
status = doGetSearchBoth(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
if (pkRange.beginKey == null || !sortedDups) {
status = doGetSearchKey(lockMode);
} else {
KeyRange.copy(pkRange.beginKey, privPKey);
status = doGetSearchBothRange(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.beginInclusive &&
pkRange.compare(privPKey, pkRange.beginKey) == 0) {
status = doGetNextDup(lockMode);
}
}
if (status == OperationStatus.SUCCESS &&
!pkRange.check(privPKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
} else if (range.singleKey) {
KeyRange.copy(range.beginKey, privKey);
status = doGetSearchKey(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
if (range.beginKey == null) {
status = doGetFirst(lockMode);
} else {
KeyRange.copy(range.beginKey, privKey);
status = doGetSearchKeyRange(lockMode);
if (status == OperationStatus.SUCCESS &&
!range.beginInclusive &&
range.compare(privKey, range.beginKey) == 0) {
status = doGetNextNoDup(lockMode);
}
}
if (status == OperationStatus.SUCCESS &&
!range.check(privKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
return status;
}
public OperationStatus getLast(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status = OperationStatus.NOTFOUND;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetLast(lockMode);
endOperation(null, status, null, null, null);
return status;
}
Cursor oldCursor = beginOperation();
try {
if (pkRange != null) {
KeyRange.copy(range.beginKey, privKey);
boolean doLast = false;
if (!sortedDups) {
status = doGetSearchKey(lockMode);
} else if (pkRange.endKey == null) {
doLast = true;
} else {
KeyRange.copy(pkRange.endKey, privPKey);
status = doGetSearchBothRange(lockMode);
if (status == OperationStatus.SUCCESS) {
if (!pkRange.endInclusive ||
pkRange.compare(pkRange.endKey, privPKey) != 0) {
status = doGetPrevDup(lockMode);
}
} else {
KeyRange.copy(range.beginKey, privKey);
doLast = true;
}
}
if (doLast) {
status = doGetSearchKey(lockMode);
if (status == OperationStatus.SUCCESS) {
status = doGetNextNoDup(lockMode);
if (status == OperationStatus.SUCCESS) {
status = doGetPrev(lockMode);
} else {
status = doGetLast(lockMode);
}
}
}
if (status == OperationStatus.SUCCESS &&
!pkRange.check(privPKey)) {
status = OperationStatus.NOTFOUND;
}
} else if (range.endKey == null) {
status = doGetLast(lockMode);
} else {
KeyRange.copy(range.endKey, privKey);
status = doGetSearchKeyRange(lockMode);
if (status == OperationStatus.SUCCESS) {
if (range.endInclusive &&
range.compare(range.endKey, privKey) == 0) {
status = doGetNextNoDup(lockMode);
if (status == OperationStatus.SUCCESS) {
status = doGetPrev(lockMode);
} else {
status = doGetLast(lockMode);
}
} else {
status = doGetPrev(lockMode);
}
} else {
status = doGetLast(lockMode);
}
}
if (status == OperationStatus.SUCCESS &&
!range.checkBegin(privKey, true)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
return status;
}
public OperationStatus getNext(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!initialized) {
return getFirst(key, pKey, data, lockMode);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetNext(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (pkRange != null) {
if (pkRange.endKey == null) {
status = doGetNextDup(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetNextDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.checkEnd(privPKey, true)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
} else if (range.singleKey) {
status = doGetNextDup(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetNext(lockMode);
if (status == OperationStatus.SUCCESS &&
!range.check(privKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
return status;
}
public OperationStatus getNextNoDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!initialized) {
return getFirst(key, pKey, data, lockMode);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetNextNoDup(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (range.singleKey) {
status = OperationStatus.NOTFOUND;
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetNextNoDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!range.check(privKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
return status;
}
public OperationStatus getPrev(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!initialized) {
return getLast(key, pKey, data, lockMode);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetPrev(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (pkRange != null) {
if (pkRange.beginKey == null) {
status = doGetPrevDup(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetPrevDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.checkBegin(privPKey, true)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
} else if (range.singleKey) {
status = doGetPrevDup(lockMode);
endOperation(null, status, key, pKey, data);
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetPrev(lockMode);
if (status == OperationStatus.SUCCESS &&
!range.check(privKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
return status;
}
public OperationStatus getPrevNoDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!initialized) {
return getLast(key, pKey, data, lockMode);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetPrevNoDup(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (range.singleKey) {
status = OperationStatus.NOTFOUND;
} else {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetPrevNoDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!range.check(privKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
}
return status;
}
public OperationStatus getSearchKey(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetSearchKey(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (!range.check(key)) {
status = OperationStatus.NOTFOUND;
} else if (pkRange != null) {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
status = doGetSearchKey(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.check(privPKey)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
} else {
shareData(key, privKey);
status = doGetSearchKey(lockMode);
endOperation(null, status, key, pKey, data);
}
return status;
}
public OperationStatus getSearchBoth(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetSearchBoth(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (!range.check(key) ||
(pkRange != null && !pkRange.check(pKey))) {
status = OperationStatus.NOTFOUND;
} else {
shareData(key, privKey);
if (secCursor != null) {
shareData(pKey, privPKey);
} else {
shareData(data, privData);
}
status = doGetSearchBoth(lockMode);
endOperation(null, status, key, pKey, data);
}
return status;
}
public OperationStatus getSearchKeyRange(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status = OperationStatus.NOTFOUND;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetSearchKeyRange(lockMode);
endOperation(null, status, null, null, null);
return status;
}
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
status = doGetSearchKeyRange(lockMode);
if (status == OperationStatus.SUCCESS &&
(!range.check(privKey) ||
(pkRange != null && !pkRange.check(pKey)))) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
return status;
}
public OperationStatus getSearchBothRange(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status = OperationStatus.NOTFOUND;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetSearchBothRange(lockMode);
endOperation(null, status, null, null, null);
return status;
}
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
if (secCursor != null) {
shareData(pKey, privPKey);
} else {
shareData(data, privData);
}
status = doGetSearchBothRange(lockMode);
if (status == OperationStatus.SUCCESS &&
(!range.check(privKey) ||
(pkRange != null && !pkRange.check(pKey)))) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
return status;
}
public OperationStatus getSearchRecordNumber(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetSearchRecordNumber(lockMode);
endOperation(null, status, null, null, null);
return status;
}
if (!range.check(key)) {
status = OperationStatus.NOTFOUND;
} else {
shareData(key, privKey);
status = doGetSearchRecordNumber(lockMode);
endOperation(null, status, key, pKey, data);
}
return status;
}
public OperationStatus getNextDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
if (!initialized) {
throw new DatabaseException("Cursor not initialized");
}
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetNextDup(lockMode);
endOperation(null, status, null, null, null);
} else if (pkRange != null && pkRange.endKey != null) {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetNextDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.checkEnd(privPKey, true)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
} else {
status = doGetNextDup(lockMode);
endOperation(null, status, key, pKey, data);
}
return status;
}
public OperationStatus getPrevDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
if (!initialized) {
throw new DatabaseException("Cursor not initialized");
}
OperationStatus status;
if (!range.hasBound()) {
setParams(key, pKey, data);
status = doGetPrevDup(lockMode);
endOperation(null, status, null, null, null);
} else if (pkRange != null && pkRange.beginKey != null) {
status = OperationStatus.NOTFOUND;
Cursor oldCursor = beginOperation();
try {
status = doGetPrevDup(lockMode);
if (status == OperationStatus.SUCCESS &&
!pkRange.checkBegin(privPKey, true)) {
status = OperationStatus.NOTFOUND;
}
} finally {
endOperation(oldCursor, status, key, pKey, data);
}
} else {
status = doGetPrevDup(lockMode);
endOperation(null, status, key, pKey, data);
}
return status;
}
public OperationStatus getCurrent(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
LockMode lockMode)
throws DatabaseException {
if (!initialized) {
throw new DatabaseException("Cursor not initialized");
}
if (secCursor != null && pKey != null) {
return secCursor.getCurrent(key, pKey, data, lockMode);
} else {
return cursor.getCurrent(key, data, lockMode);
}
}
public void close()
throws DatabaseException {
closeCursor(cursor);
}
public int count()
throws DatabaseException {
return cursor.count();
}
public OperationStatus delete()
throws DatabaseException {
return cursor.delete();
}
public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return cursor.put(key, data);
}
public OperationStatus putNoOverwrite(DatabaseEntry key,
DatabaseEntry data)
throws DatabaseException {
return cursor.putNoOverwrite(key, data);
}
public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return cursor.putNoDupData(key, data);
}
public OperationStatus putCurrent(DatabaseEntry data)
throws DatabaseException {
return cursor.putCurrent(data);
}
public OperationStatus putAfter(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return DbCompat.putAfter(cursor, key, data);
}
public OperationStatus putBefore(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return DbCompat.putBefore(cursor, key, data);
}
private OperationStatus doGetFirst(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getFirst(privKey, privPKey, privData, lockMode);
} else {
return cursor.getFirst(privKey, privData, lockMode);
}
}
private OperationStatus doGetLast(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getLast(privKey, privPKey, privData, lockMode);
} else {
return cursor.getLast(privKey, privData, lockMode);
}
}
private OperationStatus doGetNext(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getNext(privKey, privPKey, privData, lockMode);
} else {
return cursor.getNext(privKey, privData, lockMode);
}
}
private OperationStatus doGetNextDup(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getNextDup(privKey, privPKey, privData, lockMode);
} else {
return cursor.getNextDup(privKey, privData, lockMode);
}
}
private OperationStatus doGetNextNoDup(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getNextNoDup(privKey, privPKey, privData,
lockMode);
} else {
return cursor.getNextNoDup(privKey, privData, lockMode);
}
}
private OperationStatus doGetPrev(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getPrev(privKey, privPKey, privData, lockMode);
} else {
return cursor.getPrev(privKey, privData, lockMode);
}
}
private OperationStatus doGetPrevDup(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getPrevDup(privKey, privPKey, privData, lockMode);
} else {
return cursor.getPrevDup(privKey, privData, lockMode);
}
}
private OperationStatus doGetPrevNoDup(LockMode lockMode)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
return secCursor.getPrevNoDup(privKey, privPKey, privData,
lockMode);
} else {
return cursor.getPrevNoDup(privKey, privData, lockMode);
}
}
private OperationStatus doGetSearchKey(LockMode lockMode)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OperationStatus.NOTFOUND;
}
if (secCursor != null && privPKey != null) {
return secCursor.getSearchKey(privKey, privPKey, privData,
lockMode);
} else {
return cursor.getSearchKey(privKey, privData, lockMode);
}
}
private OperationStatus doGetSearchKeyRange(LockMode lockMode)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OperationStatus.NOTFOUND;
}
if (secCursor != null && privPKey != null) {
return secCursor.getSearchKeyRange(privKey, privPKey, privData,
lockMode);
} else {
return cursor.getSearchKeyRange(privKey, privData, lockMode);
}
}
private OperationStatus doGetSearchBoth(LockMode lockMode)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OperationStatus.NOTFOUND;
}
if (secCursor != null && privPKey != null) {
return secCursor.getSearchBoth(privKey, privPKey, privData,
lockMode);
} else {
return cursor.getSearchBoth(privKey, privData, lockMode);
}
}
private OperationStatus doGetSearchBothRange(LockMode lockMode)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OperationStatus.NOTFOUND;
}
if (secCursor != null && privPKey != null) {
return secCursor.getSearchBothRange(privKey, privPKey,
privData, lockMode);
} else {
return cursor.getSearchBothRange(privKey, privData, lockMode);
}
}
private OperationStatus doGetSearchRecordNumber(LockMode lockMode)
throws DatabaseException {
if (DbCompat.getRecordNumber(privKey) <= 0) {
return OperationStatus.NOTFOUND;
}
if (secCursor != null && privPKey != null) {
return DbCompat.getSearchRecordNumber(secCursor, privKey, privPKey,
privData, lockMode);
} else {
return DbCompat.getSearchRecordNumber(cursor, privKey, privData,
lockMode);
}
}
protected Cursor dupCursor(Cursor cursor, boolean samePosition)
throws DatabaseException {
return cursor.dup(samePosition);
}
protected void closeCursor(Cursor cursor)
throws DatabaseException {
cursor.close();
}
protected boolean checkRecordNumber() {
return false;
}
}