package siena.sdb;
import java.lang.reflect.Field;
import java.util.Iterator;
import siena.ClassInfo;
import siena.Query;
import siena.Util;
import siena.core.options.QueryOptionFetchType;
import siena.core.options.QueryOptionOffset;
import siena.core.options.QueryOptionPage;
import siena.core.options.QueryOptionState;
import com.amazonaws.services.simpledb.model.Item;
/**
* @author mandubian <pascal.voitot@mandubian.org>
*
* A Siena Iterable<Model> encapsulating a GAE Iterable<Entity> with its Iterator<Model>...
*/
public class SdbSienaIterable<Model> implements Iterable<Model> {
protected Iterable<Item> items;
protected Query<Model> query;
protected SdbPersistenceManager pm;
SdbSienaIterable(SdbPersistenceManager pm, Iterable<Item> items, Query<Model> query) {
this.pm = pm;
this.items = items;
this.query = query;
}
public Iterator<Model> iterator() {
return new SdbSienaIterator<Model>(query, items);
}
public class SdbSienaIterator<T> implements Iterator<T> {
Field id;
Query<T> query;
Iterator<Item> it;
int idx = 0; //used to count when in pagination
QueryOptionPage pag;
QueryOptionSdbContext sdbCtx;
QueryOptionState state;
QueryOptionOffset off;
SdbSienaIterator(Query<T> query, Iterable<Item> items) {
this.query = query;
this.id = ClassInfo.getIdField(query.getQueriedClass());
this.it = items.iterator();
// if paginating and 0 results then no more data else resets noMoreDataAfter
pag = (QueryOptionPage)query.option(QueryOptionPage.ID);
sdbCtx = (QueryOptionSdbContext)query.option(QueryOptionSdbContext.ID);
state = (QueryOptionState)query.option(QueryOptionState.ID);
// if has offset, advances in the iterator
off = (QueryOptionOffset)query.option(QueryOptionOffset.ID);
if(off!=null && off.offset != 0){
for(int i=0; i<off.offset; i++){
if(it.hasNext()){
it.next();
}
}
// moves real offset if not paginating
//if(!pag.isPaginating())
// sdbCtx.realOffset += off.offset;
}
if(pag.isPaginating()){
if(!it.hasNext()){
sdbCtx.noMoreDataAfter = true;
}else {
sdbCtx.noMoreDataAfter = false;
}
}
}
public boolean hasNext() {
boolean n = it.hasNext();
if(!n){
//pm.postMapping(query);
int pageSize = sdbCtx.realPageSize-idx;
if(!pag.isPaginating()){
// not paginating = we get current token as the move has already been done
// tries to fetch new items if has a live token
String token = null;
if(state.isStateful()){
token = sdbCtx.currentToken();
if(!pag.isActive()){
pageSize = Integer.MAX_VALUE;
}
}
else {
token = sdbCtx.nextToken();
if(pag.isActive()){
pag.passivate();
pag.pageSize = 0;
}else {
pageSize = Integer.MAX_VALUE;
}
if(off.isActive()){
off.passivate();
off.offset = 0;
}
}
if(token != null && pageSize > 0){
SdbSienaIterable<Model> iter =
(SdbSienaIterable<Model>)pm.doFetchIterable(query, pageSize, 0, true);
items = iter.items;
it = items.iterator();
idx = 0;
return it.hasNext();
}else if(token == null){
sdbCtx.noMoreDataAfter = true;
}
}else {
// paginating = if has next token & not already at the last element of the page
// moves to next token (but not before verifying it has next token)
boolean b = sdbCtx.hasNextToken();
if(b && pageSize > 0){
sdbCtx.nextToken();
SdbSienaIterable<Model> iter =
(SdbSienaIterable<Model>)pm.doFetchIterable(query, pageSize, 0, true);
items = iter.items;
it = items.iterator();
idx = 0;
return it.hasNext();
}else if(!b){
sdbCtx.noMoreDataAfter = true;
}
}
return false;
}
return true;
}
public T next() {
Item item = it.next();
idx++;
// moves realoffset if not paginating
//if(!pag.isPaginating()){
// sdbCtx.realOffset++;
//}
Class<T> clazz = query.getQueriedClass();
QueryOptionFetchType fetchType = (QueryOptionFetchType)query.option(QueryOptionFetchType.ID);
T obj = Util.createObjectInstance(clazz);
switch(fetchType.fetchType){
case KEYS_ONLY:
SdbMappingUtils.fillModelKeysOnly(item, clazz, ClassInfo.getClassInfo(clazz), obj);
case NORMAL:
default:
SdbMappingUtils.fillModel(item, clazz, ClassInfo.getClassInfo(clazz), obj);
// join management
if(!query.getJoins().isEmpty()
|| !ClassInfo.getClassInfo(query.getQueriedClass()).joinFields.isEmpty())
pm.mapJoins(query, obj);
}
return obj;
}
public void remove() {
it.remove();
}
}
}