package net.notdot.bdbdatastore.server;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.notdot.bdbdatastore.Indexing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.appengine.entity.Entity;
import com.google.protobuf.InvalidProtocolBufferException;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
public abstract class AbstractDatastoreResultSet {
static final Logger logger = LoggerFactory.getLogger(DatastorePKResultSet.class);
private int remaining = 0;
private Set<Indexing.EntityKey> seen = new HashSet<Indexing.EntityKey>();
private boolean initialized = false;
protected AppDatastore ds;
protected QuerySpec query;
protected DatabaseEntry currentValue = null;
protected DatabaseEntry currentPKey = new DatabaseEntry();
protected Indexing.EntityKey currentPKeyEnt = null;
public AbstractDatastoreResultSet(AppDatastore ds, QuerySpec query) throws DatabaseException {
this.ds = ds;
this.query = query;
if(query != null)
this.remaining = query.getOffset() + query.getLimit();
}
// Opens the internal cursor for reading, positioning it for the next result
protected abstract void openCursor() throws DatabaseException;
// Closes the internal cursor
protected abstract void closeCursor() throws DatabaseException;
// Reads the next record. Must set currentValue to the new record's value; may set
// currentKey to the new record's key.
protected abstract boolean readInternal() throws DatabaseException, InvalidProtocolBufferException;
protected boolean read() throws DatabaseException {
if(!initialized) {
initialized = true;
this.skip(this.query.getOffset());
}
if(remaining == 0)
return false;
while(true) {
try {
currentPKeyEnt = null;
if(!this.readInternal()) {
remaining = 0;
return false;
}
if(currentPKeyEnt == null)
currentPKeyEnt = Indexing.EntityKey.parseFrom(currentPKey.getData());
if(!seen.contains(currentPKeyEnt)) {
seen.add(currentPKeyEnt);
remaining--;
return true;
}
} catch (InvalidProtocolBufferException e) {
//TODO: Make this message more helpful somehow.
logger.error("Invalid protocol buffer encountered");
}
}
}
protected void skip(int count) throws DatabaseException {
for(int i = 0; i < count; i++) {
if(!this.read())
break;
}
}
public boolean hasMore() {
return remaining != 0;
}
public List<Entity.EntityProto> getNext(int count) throws DatabaseException {
List<Entity.EntityProto> entities = new ArrayList<Entity.EntityProto>(count);
for(int i = 0; i < count; i++) {
if(this.hasMore() && this.read()) {
assert this.currentValue != null;
try {
Entity.EntityProto entity = Indexing.EntityData.parseFrom(currentValue.getData()).getData();
entities.add(entity);
} catch(InvalidProtocolBufferException ex) {
//TODO: Make this message more helpful somehow.
logger.error("Invalid protocol buffer encountered");
}
}
}
return entities;
}
}