package org.molgenis.util;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Iterable that returns an iterator that retrieves a new batch of objects after a given batchSize
*
* @param <T>
*/
public abstract class BatchingIterable<T> implements Iterable<T>
{
private final int batchSize;
private final int offset;
/**
* Limit > 0: Number of elements to retrieve, Limit = 0: Limit undefined
*/
private final int limit;
public BatchingIterable(int batchSize)
{
this(batchSize, 0, 0);
}
public BatchingIterable(int batchSize, int offset, int limit)
{
if (batchSize <= 0) throw new IllegalArgumentException("BatchSize must be greated then 0");
if (offset < 0) throw new IllegalArgumentException("Offset must be larger than or equal to 0");
if (limit < 0) throw new IllegalArgumentException("Limit must be larger than or equal to 0");
this.batchSize = batchSize;
this.offset = offset;
this.limit = limit;
}
@Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
/**
* Element index
*/
private int index = offset;
/**
* Element iterator for the current batch
*/
private Iterator<T> it;
@Override
public boolean hasNext()
{
boolean hasNext;
// lazy load first batch
if (it == null)
{
it = nextBatch();
hasNext = it.hasNext();
if (!hasNext)
{
return false;
}
}
else
{
hasNext = it.hasNext();
}
if (!hasNext)
{
it = nextBatch();
hasNext = it.hasNext();
}
return hasNext;
}
@Override
public T next()
{
if (!hasNext())
{
throw new NoSuchElementException();
}
T element = it.next();
++index;
return element;
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
private Iterator<T> nextBatch()
{
// calculate batch size
int nextBatchSize;
// always retrieve first batch: index == offset
// retrieve next batch if previous batch contained less items then batch size
if (index == offset || (index - offset) % batchSize == 0)
{
if (limit == 0)
{
nextBatchSize = batchSize;
}
else
{
if (index == offset + limit)
{
nextBatchSize = 0;
}
else if (index + batchSize <= offset + limit)
{
nextBatchSize = batchSize;
}
else
{
nextBatchSize = offset + limit - index;
}
}
}
else
{
nextBatchSize = 0;
}
if (nextBatchSize == 0)
{
return Collections.emptyIterator();
}
else
{
return getBatch(index, nextBatchSize).iterator();
}
}
};
}
/**
* Return new batch, should not return null but empty list if no more elements are available
*
* @param offset (startIndex)
* @param batchSize
* @return
*/
protected abstract Iterable<T> getBatch(int offset, int batchSize);
}