package net.databinder.models;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
/**
* Projects a single list into multiple, arbitrarily transformed sublists without replicating
* the list structure. A parent list containing sublists is the object wrapped in this model,
* while the master list model passed in is held and used internally.
* <p>If you need to detach the master list, detach this model and the command will be
* passed to the contained master list. Any action that changes the size of the master
* list must also detach this model so it can recalculate the sublist count.
*
* @author Nathan Hamblen
*/
public abstract class SublistProjectionModel extends LoadableDetachableModel {
/** Continuous list used to feed this model's sublists. */
private IModel master;
public SublistProjectionModel(IModel master) {
this.master = master;
}
/** @return number of sublists */
protected abstract int getParentSize();
/** @return index of master list mapped to parameters */
protected abstract int transform(int parentIdx, int sublistIdx);
/** @return size sublist at given index*/
protected abstract int getSize(int parentIdx);
/**
* Breaks the parent list into chunks of the requested size, in the same order as
* the parent list.
*/
public static class Chunked extends SublistProjectionModel {
protected int chunkSize;
public Chunked(int chunkSize, IModel master) {
super(master);
this.chunkSize = chunkSize;
}
protected int transform(int parentIdx, int sublistIdx) {
return parentIdx * chunkSize + sublistIdx;
}
protected int getSize(int parentIdx) {
return Math.min(getMasterList().size() - parentIdx * chunkSize,
chunkSize);
}
protected int getParentSize() {
return (getMasterList().size() - 1) / chunkSize + 1;
}
}
/**
* Transposes rows and columns so the list runs top to bottom rather than
* left to right.
*/
public static class Transposed extends Chunked {
public Transposed(int columns, IModel master) {
super(columns, master);
}
protected int transform(int parentIdx, int sublistIdx) {
return parentIdx + sublistIdx * getParentSize();
}
protected int getSize(int parentIdx) {
return (getMasterList().size() - parentIdx - 1) / getParentSize() + 1;
}
}
protected List getMasterList() {
return (List) master.getObject();
}
@Override
protected List<List> load() {
int rows = getParentSize();
List<List> parent = new ArrayList<List>(rows);
for (int i = 0; i < rows; i++)
parent.add(new ProjectedSublist(i));
return parent;
}
/**
* This is a virtual list, a projection of the master list. Its size and index trasform is
* governed by the containing object.
*/
@SuppressWarnings("unchecked")
protected class ProjectedSublist extends AbstractList {
private int parentIdx;
public ProjectedSublist(final int parentIdx) {
this.parentIdx = parentIdx;
}
@Override
public Object get(final int index) {
return getMasterList().get(transform(parentIdx, index));
}
@Override
public int size() {
return getSize(parentIdx);
}
}
/**
* Detach master list.
*/
@Override
protected void onDetach() {
master.detach();
}
}