package com.sleepycat.persist;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.sleepycat.bind.EntityBinding;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.db.Cursor;
import com.sleepycat.db.CursorConfig;
import com.sleepycat.db.Database;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.JoinCursor;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.db.Transaction;
public class EntityJoin<PK,E> {
private PrimaryIndex<PK,E> primary;
private List<Condition> conditions;
public EntityJoin(PrimaryIndex<PK,E> index) {
primary = index;
conditions = new ArrayList<Condition>();
}
public <SK> void addCondition(SecondaryIndex<SK,PK,E> index, SK key) {
DatabaseEntry keyEntry = new DatabaseEntry();
index.getKeyBinding().objectToEntry(key, keyEntry);
Database db = index.getKeysDatabase();
if (db == null) {
db = index.getDatabase();
}
conditions.add(new Condition(db, keyEntry));
}
public ForwardCursor<E> entities()
throws DatabaseException {
return entities(null, null);
}
public ForwardCursor<E> entities(Transaction txn, CursorConfig config)
throws DatabaseException {
return new JoinForwardCursor<E>(txn, config, false);
}
public ForwardCursor<PK> keys()
throws DatabaseException {
return keys(null, null);
}
public ForwardCursor<PK> keys(Transaction txn, CursorConfig config)
throws DatabaseException {
return new JoinForwardCursor<PK>(txn, config, true);
}
private static class Condition {
private Database db;
private DatabaseEntry key;
Condition(Database db, DatabaseEntry key) {
this.db = db;
this.key = key;
}
Cursor openCursor(Transaction txn, CursorConfig config)
throws DatabaseException {
OperationStatus status;
Cursor cursor = db.openCursor(txn, config);
try {
DatabaseEntry data = BasicIndex.NO_RETURN_ENTRY;
status = cursor.getSearchKey(key, data, null);
} catch (DatabaseException e) {
try {
cursor.close();
} catch (DatabaseException ignored) {}
throw e;
}
if (status == OperationStatus.SUCCESS) {
return cursor;
} else {
cursor.close();
return null;
}
}
}
private class JoinForwardCursor<V> implements ForwardCursor<V> {
private Cursor[] cursors;
private JoinCursor joinCursor;
private boolean doKeys;
JoinForwardCursor(Transaction txn, CursorConfig config, boolean doKeys)
throws DatabaseException {
this.doKeys = doKeys;
try {
cursors = new Cursor[conditions.size()];
for (int i = 0; i < cursors.length; i += 1) {
Condition cond = conditions.get(i);
Cursor cursor = cond.openCursor(txn, config);
if (cursor == null) {
doClose(null);
return;
}
cursors[i] = cursor;
}
joinCursor = primary.getDatabase().join(cursors, null);
} catch (DatabaseException e) {
doClose(e);
}
}
public V next()
throws DatabaseException {
return next(null);
}
public V next(LockMode lockMode)
throws DatabaseException {
if (joinCursor == null) {
return null;
}
if (doKeys) {
DatabaseEntry key = new DatabaseEntry();
OperationStatus status = joinCursor.getNext(key, lockMode);
if (status == OperationStatus.SUCCESS) {
EntryBinding binding = primary.getKeyBinding();
return (V) binding.entryToObject(key);
}
} else {
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
OperationStatus status =
joinCursor.getNext(key, data, lockMode);
if (status == OperationStatus.SUCCESS) {
EntityBinding binding = primary.getEntityBinding();
return (V) binding.entryToObject(key, data);
}
}
return null;
}
public Iterator<V> iterator() {
return iterator(null);
}
public Iterator<V> iterator(LockMode lockMode) {
return new BasicIterator<V>(this, lockMode);
}
public void close()
throws DatabaseException {
doClose(null);
}
private void doClose(DatabaseException firstException)
throws DatabaseException {
if (joinCursor != null) {
try {
joinCursor.close();
joinCursor = null;
} catch (DatabaseException e) {
if (firstException == null) {
firstException = e;
}
}
}
for (int i = 0; i < cursors.length; i += 1) {
Cursor cursor = cursors[i];
if (cursor != null) {
try {
cursor.close();
cursors[i] = null;
} catch (DatabaseException e) {
if (firstException == null) {
firstException = e;
}
}
}
}
if (firstException != null) {
throw firstException;
}
}
}
}