package com.sleepycat.bdb;
import com.sleepycat.db.Db;
import com.sleepycat.db.Dbc;
import com.sleepycat.db.DbException;
import com.sleepycat.db.Dbt;
final class KeyRange {
private static final int EINVAL = 22;
private DataThang beginKey;
private DataThang endKey;
private boolean isSingleKey;
private boolean isCursorValid;
public KeyRange() {
}
public KeyRange(DataThang singleKey) {
this.beginKey = singleKey;
isSingleKey = true;
}
public KeyRange(DataThang beginKey, boolean beginInclusive,
DataThang endKey, boolean endInclusive) {
if (beginKey != null) {
this.beginKey = beginKey;
if (!beginInclusive)
this.beginKey.increment();
}
if (endKey != null) {
this.endKey = endKey;
if (endInclusive)
this.endKey.increment();
}
}
public KeyRange(KeyRange other) {
this.beginKey = other.beginKey;
this.endKey = other.endKey;
this.isSingleKey = other.isSingleKey;
}
public KeyRange subRange(DataThang singleKey)
throws KeyRangeException {
if (!check(singleKey)) {
throw new KeyRangeException("singleKey out of range");
}
return new KeyRange(singleKey);
}
public KeyRange subRange(DataThang beginKey, boolean beginInclusive,
DataThang endKey, boolean endInclusive)
throws KeyRangeException {
KeyRange range = new KeyRange(beginKey, beginInclusive,
endKey, endInclusive);
if (range.beginKey == null) {
range.beginKey = this.beginKey;
} else if (!check(range.beginKey)) {
throw new KeyRangeException("beginKey out of range");
}
if (range.endKey == null) {
range.endKey = this.endKey;
} else if (!checkRangeEnd(range.endKey)) {
throw new KeyRangeException("endKey out of range");
}
return range;
}
public final DataThang getSingleKey() {
return isSingleKey ? beginKey : null;
}
final boolean hasBound() {
return isSingleKey || endKey != null || beginKey != null;
}
public String toString() {
return "[KeyRange " + beginKey + ' ' + endKey +
(isSingleKey ? " single" : "");
}
public boolean check(DataThang key) {
if (isSingleKey)
return (key.compareTo(beginKey) == 0);
if ((beginKey != null) && (key.compareTo(beginKey) < 0))
return false;
if ((endKey != null) && (key.compareTo(endKey) >= 0))
return false;
return true;
}
private boolean checkRangeEnd(DataThang key) {
if (isSingleKey)
return (key.compareTo(beginKey) == 0);
if ((beginKey != null) && (key.compareTo(beginKey) < 0))
return false;
if ((endKey != null) && (key.compareTo(endKey) > 0))
return false;
return true;
}
public int get(DataDb db, Dbc cursor, DataThang key,
DataThang data, int flags)
throws DbException {
if (beginKey == null && endKey == null) {
return db.get(cursor, key, data, flags);
}
int extraFlags = flags & DataDb.FLAGS_MOD_MASK;
int pos = flags & DataDb.FLAGS_POS_MASK;
int origPos = pos;
int err = 0;
boolean wasInvalid = !isCursorValid;
if (pos == Db.DB_CURRENT || pos == Db.DB_NEXT_DUP) {
if (wasInvalid) throwInvalid(key, data);
err = db.get(cursor, key, data, pos | extraFlags);
} else if (pos == Db.DB_SET || pos == Db.DB_SET_RANGE ||
pos == Db.DB_SET_RECNO || pos == Db.DB_GET_BOTH) {
if (pos != Db.DB_SET_RANGE && !check(key)) {
err = setInvalid(key, data);
} else {
err = db.get(cursor, key, data, pos | extraFlags);
if (err == 0 && pos == Db.DB_SET_RANGE) {
if (!check(key)) err = setInvalid(key, data);
}
}
} else if (pos == Db.DB_FIRST || pos == Db.DB_NEXT ||
pos == Db.DB_NEXT_NODUP) {
if (wasInvalid) pos = Db.DB_FIRST;
if (isSingleKey) {
if (pos == Db.DB_NEXT_NODUP) {
err = Db.DB_NOTFOUND;
} else if (pos == Db.DB_FIRST) {
key.copy(beginKey);
err = db.get(cursor, key, data, Db.DB_SET | extraFlags);
} else {
err = db.get(cursor, key, data,
Db.DB_NEXT_DUP | extraFlags);
}
} else {
if (beginKey == null) {
err = db.get(cursor, key, data, pos | extraFlags);
} else {
if (pos == Db.DB_FIRST) {
key.copy(beginKey);
err = db.get(cursor, key, data,
Db.DB_SET_RANGE | extraFlags);
} else {
err = db.get(cursor, key, data, pos | extraFlags);
}
}
if (err == 0 && !check(key)) {
if (pos == Db.DB_FIRST) {
err = setInvalid(key, data);
} else {
err = db.get(cursor, key, data,
(pos == Db.DB_NEXT_NODUP)
? Db.DB_PREV_NODUP : Db.DB_PREV);
if (err != 0) {
throw new DbException("Range internal error", err);
}
err = Db.DB_NOTFOUND;
}
}
}
} else if (pos == Db.DB_LAST || pos == Db.DB_PREV ||
pos == Db.DB_PREV_NODUP) {
if (wasInvalid) pos = Db.DB_LAST;
if (isSingleKey) {
if (pos == Db.DB_PREV_NODUP) {
err = Db.DB_NOTFOUND;
} else if (endKey == null) {
endKey = new DataThang(beginKey);
endKey.increment();
}
}
if (err != 0) {
} else if (endKey == null) {
err = db.get(cursor, key, data, pos | extraFlags);
} else {
if (pos == Db.DB_LAST) {
key.copy(endKey);
err = db.get(cursor, key, data,
Db.DB_SET_RANGE | extraFlags);
if (err == 0) {
err = db.get(cursor, key, data,
((origPos == Db.DB_PREV_NODUP)
? Db.DB_PREV_NODUP : Db.DB_PREV)
| extraFlags);
} else {
err = db.get(cursor, key, data,
Db.DB_LAST | extraFlags);
}
} else {
err = db.get(cursor, key, data, pos | extraFlags);
}
}
if (err == 0 && beginKey != null) {
int compare = key.compareTo(beginKey);
if (isSingleKey ? (compare != 0) : (compare < 0)) {
if (pos == Db.DB_LAST) {
err = setInvalid(key, data);
} else {
err = db.get(cursor, key, data,
(pos == Db.DB_PREV_NODUP)
? Db.DB_NEXT_NODUP : Db.DB_NEXT);
if (err != 0) {
throw new DbException("Range internal error", err);
}
err = Db.DB_NOTFOUND;
}
}
}
} else if (pos == Db.DB_CONSUME) {
err = db.get(cursor, key, data, flags);
} else {
throw new DbException("Unsupported flag", EINVAL);
}
if (err == 0) isCursorValid = true;
return err;
}
private void throwInvalid(DataThang key, DataThang data)
throws DbException {
setInvalid(key, data);
throw new DbException("Cursor not initialized", EINVAL);
}
private int setInvalid(DataThang key, DataThang data) {
isCursorValid = false;
if (key != null) key.set_size(0);
if (data != null) data.set_size(0);
return Db.DB_NOTFOUND;
}
}