BlockIterator.java [plain text]
package com.sleepycat.collections;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.util.keyrange.KeyRange;
class BlockIterator implements BaseIterator {
private StoredCollection coll;
private boolean writeAllowed;
private byte[][] keys;
private byte[][] priKeys;
private byte[][] values;
private int nextIndex;
private int dataIndex;
private Object dataObject;
BlockIterator(StoredCollection coll, boolean writeAllowed, int blockSize) {
this.coll = coll;
this.writeAllowed = writeAllowed;
keys = new byte[blockSize][];
priKeys = coll.isSecondary() ? (new byte[blockSize][]) : keys;
values = new byte[blockSize][];
nextIndex = blockSize;
dataIndex = -1;
dataObject = null;
}
private BlockIterator(BlockIterator o) {
coll = o.coll;
writeAllowed = o.writeAllowed;
keys = copyArray(o.keys);
priKeys = coll.isSecondary() ? copyArray(o.priKeys) : keys;
values = copyArray(o.values);
nextIndex = o.nextIndex;
dataIndex = o.dataIndex;
dataObject = o.dataObject;
}
private byte[][] copyArray(byte[][] a) {
byte[][] b = new byte[a.length][];
for (int i = 0; i < b.length; i += 1) {
if (a[i] != null) {
b[i] = KeyRange.copyBytes(a[i]);
}
}
return b;
}
private boolean isNextAvailable() {
return (nextIndex < keys.length) &&
(keys[nextIndex] != null);
}
private boolean isPrevAvailable() {
return (nextIndex > 0) &&
(keys[nextIndex - 1] != null);
}
private int getRecordNumber(int i) {
if (coll.view.btreeRecNumDb) {
DataCursor cursor = null;
try {
cursor = new DataCursor(coll.view, false);
if (moveCursor(i, cursor)) {
return cursor.getCurrentRecordNumber();
} else {
throw new IllegalStateException();
}
} catch (DatabaseException e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
} else {
DatabaseEntry entry = new DatabaseEntry(keys[i]);
return DbCompat.getRecordNumber(entry);
}
}
private void makeDataObject() {
int i = dataIndex;
DatabaseEntry keyEntry = new DatabaseEntry(keys[i]);
DatabaseEntry priKeyEntry = (keys != priKeys)
? (new DatabaseEntry(priKeys[i]))
: keyEntry;
DatabaseEntry valuesEntry = new DatabaseEntry(values[i]);
dataObject = coll.makeIteratorData(this, keyEntry, priKeyEntry,
valuesEntry);
}
private void clearSlots() {
for (int i = 0; i < keys.length; i += 1) {
keys[i] = null;
priKeys[i] = null;
values[i] = null;
}
}
private void setSlot(int i, DataCursor cursor) {
keys[i] = KeyRange.getByteArray(cursor.getKeyThang());
if (keys != priKeys) {
priKeys[i] = KeyRange.getByteArray
(cursor.getPrimaryKeyThang());
}
values[i] = KeyRange.getByteArray(cursor.getValueThang());
}
private void insertSlot(int i, DataCursor cursor) {
if (i < keys.length) {
for (int j = keys.length - 1; j > i; j -= 1) {
keys[j] = keys[j - 1];
priKeys[j] = priKeys[j - 1];
values[j] = values[j - 1];
if (coll.view.recNumRenumber && keys[j] != null) {
bumpRecordNumber(j);
}
}
nextIndex += 1;
} else {
if (i != keys.length) {
throw new IllegalStateException();
}
i -= 1;
for (int j = 0; j < i; j += 1) {
keys[j] = keys[j + 1];
priKeys[j] = priKeys[j + 1];
values[j] = values[j + 1];
}
}
setSlot(i, cursor);
dataIndex = -1;
}
private void bumpRecordNumber(int i) {
DatabaseEntry entry = new DatabaseEntry(keys[i]);
DbCompat.setRecordNumber(entry,
DbCompat.getRecordNumber(entry) + 1);
keys[i] = entry.getData();
}
private void deleteSlot(int i) {
for (int j = i + 1; j < keys.length; j += 1) {
keys[j - 1] = keys[j];
priKeys[j - 1] = priKeys[j];
values[j - 1] = values[j];
}
int last = keys.length - 1;
keys[last] = null;
priKeys[last] = null;
values[last] = null;
if (nextIndex > i) {
nextIndex -= 1;
}
dataIndex = -1;
}
private boolean moveCursor(int i, DataCursor cursor)
throws DatabaseException {
return cursor.repositionExact(keys[i], priKeys[i], values[i], false);
}
private void closeCursor(DataCursor cursor) {
if (cursor != null) {
try {
cursor.close();
} catch (DatabaseException e) {
throw StoredContainer.convertException(e);
}
}
}
public boolean hasNext() {
if (isNextAvailable()) {
return true;
}
DataCursor cursor = null;
try {
cursor = new DataCursor(coll.view, writeAllowed);
int prev = nextIndex - 1;
boolean found = false;
if (keys[prev] == null) {
OperationStatus status = cursor.getFirst(false);
if (status == OperationStatus.SUCCESS) {
found = true;
nextIndex = 0;
}
} else {
int repos = cursor.repositionRange
(keys[prev], priKeys[prev], values[prev], false);
if (repos == DataCursor.REPOS_EXACT) {
found = true;
nextIndex = 1;
if (dataIndex == prev) {
dataIndex = 0;
} else {
dataIndex = -1;
dataObject = null;
}
} else if (repos == DataCursor.REPOS_NEXT) {
found = true;
nextIndex = 0;
dataIndex = -1;
dataObject = null;
} else {
if (repos != DataCursor.REPOS_EOF) {
throw new IllegalStateException();
}
}
}
if (found) {
clearSlots();
int i = 0;
boolean done = false;
while (!done) {
setSlot(i, cursor);
i += 1;
if (i < keys.length) {
OperationStatus status = coll.iterateDuplicates() ?
cursor.getNext(false) :
cursor.getNextNoDup(false);
if (status != OperationStatus.SUCCESS) {
done = true;
}
} else {
done = true;
}
}
}
return isNextAvailable();
} catch (DatabaseException e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
public boolean hasPrevious() {
if (isPrevAvailable()) {
return true;
}
if (!isNextAvailable()) {
return false;
}
DataCursor cursor = null;
try {
cursor = new DataCursor(coll.view, writeAllowed);
int last = keys.length - 1;
int next = nextIndex;
boolean found = false;
int repos = cursor.repositionRange
(keys[next], priKeys[next], values[next], false);
if (repos == DataCursor.REPOS_EXACT ||
repos == DataCursor.REPOS_NEXT) {
found = true;
nextIndex = last;
if (dataIndex == next && repos == DataCursor.REPOS_EXACT) {
dataIndex = last;
} else {
dataIndex = -1;
dataObject = null;
}
} else {
if (repos != DataCursor.REPOS_EOF) {
throw new IllegalStateException();
}
}
if (found) {
clearSlots();
int i = last;
boolean done = false;
while (!done) {
setSlot(i, cursor);
i -= 1;
if (i >= 0) {
OperationStatus status = coll.iterateDuplicates() ?
cursor.getPrev(false) :
cursor.getPrevNoDup(false);
if (status != OperationStatus.SUCCESS) {
done = true;
}
} else {
done = true;
}
}
}
return isPrevAvailable();
} catch (DatabaseException e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
public Object next() {
if (hasNext()) {
dataIndex = nextIndex;
nextIndex += 1;
makeDataObject();
return dataObject;
} else {
throw new NoSuchElementException();
}
}
public Object previous() {
if (hasPrevious()) {
nextIndex -= 1;
dataIndex = nextIndex;
makeDataObject();
return dataObject;
} else {
throw new NoSuchElementException();
}
}
public int nextIndex() {
if (!coll.view.recNumAccess) {
throw new UnsupportedOperationException(
"Record number access not supported");
}
return hasNext() ? (getRecordNumber(nextIndex) -
coll.getIndexOffset())
: Integer.MAX_VALUE;
}
public int previousIndex() {
if (!coll.view.recNumAccess) {
throw new UnsupportedOperationException(
"Record number access not supported");
}
return hasPrevious() ? (getRecordNumber(nextIndex - 1) -
coll.getIndexOffset())
: (-1);
}
public void set(Object value) {
if (dataObject == null) {
throw new IllegalStateException();
}
if (!coll.hasValues()) {
throw new UnsupportedOperationException();
}
DataCursor cursor = null;
boolean doAutoCommit = coll.beginAutoCommit();
try {
cursor = new DataCursor(coll.view, writeAllowed);
if (moveCursor(dataIndex, cursor)) {
cursor.putCurrent(value);
setSlot(dataIndex, cursor);
coll.closeCursor(cursor);
coll.commitAutoCommit(doAutoCommit);
} else {
throw new IllegalStateException();
}
} catch (Exception e) {
coll.closeCursor(cursor);
throw coll.handleException(e, doAutoCommit);
}
}
public void remove() {
if (dataObject == null) {
throw new IllegalStateException();
}
DataCursor cursor = null;
boolean doAutoCommit = coll.beginAutoCommit();
try {
cursor = new DataCursor(coll.view, writeAllowed);
if (moveCursor(dataIndex, cursor)) {
cursor.delete();
deleteSlot(dataIndex);
dataObject = null;
if (nextIndex == 0 && keys[0] == null) {
OperationStatus status;
for (int i = 0; i < keys.length; i += 1) {
status = coll.iterateDuplicates() ?
cursor.getNext(false) :
cursor.getNextNoDup(false);
if (status == OperationStatus.SUCCESS) {
setSlot(i, cursor);
} else {
break;
}
}
if (keys[0] == null) {
nextIndex = keys.length;
for (int i = nextIndex - 1; i >= 0; i -= 1) {
status = coll.iterateDuplicates() ?
cursor.getPrev(false) :
cursor.getPrevNoDup(false);
if (status == OperationStatus.SUCCESS) {
setSlot(i, cursor);
} else {
break;
}
}
}
}
coll.closeCursor(cursor);
coll.commitAutoCommit(doAutoCommit);
} else {
throw new IllegalStateException();
}
} catch (Exception e) {
coll.closeCursor(cursor);
throw coll.handleException(e, doAutoCommit);
}
}
public void add(Object value) {
coll.checkIterAddAllowed();
OperationStatus status = OperationStatus.SUCCESS;
DataCursor cursor = null;
boolean doAutoCommit = coll.beginAutoCommit();
try {
if (coll.view.keysRenumbered || !coll.areDuplicatesOrdered()) {
boolean hasPrev = hasPrevious();
if (!hasPrev && !hasNext()) {
if (coll.view.keysRenumbered) {
status = coll.view.append(value, null, null);
} else if (coll.view.dupsAllowed &&
coll.view.range.isSingleKey()) {
cursor = new DataCursor(coll.view, writeAllowed);
cursor.useRangeKey();
status = cursor.putNoDupData(null, value, null, true);
coll.closeCursor(cursor);
cursor = null;
} else {
throw new IllegalStateException
("Collection is empty, cannot add() duplicate");
}
if (status == OperationStatus.SUCCESS) {
next();
dataIndex = nextIndex - 1;
}
} else {
cursor = new DataCursor(coll.view, writeAllowed);
int insertIndex = hasPrev ? (nextIndex - 1) : nextIndex;
if (!moveCursor(insertIndex, cursor)) {
throw new IllegalStateException();
}
status = hasPrev ? cursor.putAfter(value)
: cursor.putBefore(value);
if (status == OperationStatus.SUCCESS) {
insertSlot(nextIndex, cursor);
}
}
} else {
cursor = new DataCursor(coll.view, writeAllowed);
if (coll.view.range.isSingleKey()) {
cursor.useRangeKey();
} else {
if (dataIndex < 0 || !moveCursor(dataIndex, cursor)) {
throw new IllegalStateException();
}
}
status = cursor.putNoDupData(null, value, null, true);
if (status == OperationStatus.SUCCESS) {
clearSlots();
setSlot(0, cursor);
dataIndex = 0;
nextIndex = 1;
}
}
if (status == OperationStatus.KEYEXIST) {
throw new IllegalArgumentException("Duplicate value");
} else if (status != OperationStatus.SUCCESS) {
throw new IllegalArgumentException("Could not insert: " +
status);
}
dataObject = null;
coll.closeCursor(cursor);
coll.commitAutoCommit(doAutoCommit);
} catch (Exception e) {
coll.closeCursor(cursor);
throw coll.handleException(e, doAutoCommit);
}
}
public final ListIterator dup() {
return new BlockIterator(this);
}
public final boolean isCurrentData(Object currentData) {
return (dataObject == currentData);
}
public final boolean moveToIndex(int index) {
DataCursor cursor = null;
try {
cursor = new DataCursor(coll.view, writeAllowed);
OperationStatus status =
cursor.getSearchKey(new Integer(index), null, false);
if (status == OperationStatus.SUCCESS) {
clearSlots();
setSlot(0, cursor);
nextIndex = 0;
return true;
} else {
return false;
}
} catch (DatabaseException e) {
throw StoredContainer.convertException(e);
} finally {
closeCursor(cursor);
}
}
}