package edu.ualberta.med.biobank.common.wrappers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* For Queue-ing up modifications to collections so that the modifications can
* be applied only if and when a collection property is accessed through a
* getter. This is to avoid elements causing their collector to potentially
* expensively load all its sister elements when bi-directionally setting a
* link.
*
* @author jferland
*
* @param <E>
*/
public class ElementQueue<E> {
private final Map<Property<?, ? super E>, List<Action>> map = new HashMap<Property<?, ? super E>, List<Action>>();
private final ModelWrapper<E> wrapper;
public ElementQueue(ModelWrapper<E> wrapper) {
this.wrapper = wrapper;
}
/**
* Adds actions/ commands to remember to add the given {@link ModelWrapper}
* elements to the given collection {@link Property} when it is loaded.
*
* @param <W> wrapper class
* @param <M> model class wrapped by {@link W}
* @param property
* @param elements
*/
public <W extends ModelWrapper<? extends M>, M> void add(
Property<? extends Collection<? extends M>, ? super E> property,
Collection<W> elements) {
List<Action> actions = getActions(property);
for (W element : elements) {
actions.add(new Action(Action.Type.ADD, element));
}
}
/**
* Adds actions/ commands to remember to remove the given
* {@link ModelWrapper} elements to the given collection {@link Property}
* when it is loaded.
*
* @param <W> wrapper class
* @param <M> model class wrapped by {@link W}
* @param property
* @param wrappers
*/
public <W extends ModelWrapper<? extends M>, M> void remove(
Property<? extends Collection<? extends M>, ? super E> property,
Collection<W> wrappers) {
List<Action> actions = getActions(property);
for (W wrapper : wrappers) {
actions.add(new Action(Action.Type.REMOVE, wrapper));
}
}
/**
* Flushes the {@link List} of {@link Action}-s for the given collection
* {@link Property} into the given {@link Collection}. That is, perform the
* queued up actions (e.g. add elements, delete elements) then clear the
* action queue.
*
* @param <W> wrapper class
* @param <M> model class wrapped by {@link W}
* @param property
*/
public <W extends ModelWrapper<? extends M>, M> void flush(
Property<? extends Collection<? extends M>, ? super E> property) {
@SuppressWarnings("unchecked")
Collection<W> collection = (Collection<W>) wrapper
.recallProperty(property);
List<Action> actions = getActions(property);
for (Action action : actions) {
@SuppressWarnings("unchecked")
W wrapper = (W) action.wrapper;
if (action.type == Action.Type.ADD) {
// remove then add in case the element already exists
collection.remove(wrapper);
collection.add(wrapper);
} else if (action.type == Action.Type.REMOVE) {
collection.remove(action.wrapper);
}
}
map.remove(property);
}
public <T> Collection<ModelWrapper<T>> getAdded(
Property<? extends Collection<? extends T>, ? super E> property) {
Collection<ModelWrapper<T>> added = new ArrayList<ModelWrapper<T>>();
List<Action> actions = getActions(property);
for (Action action : actions) {
@SuppressWarnings("unchecked")
ModelWrapper<T> wrapper = (ModelWrapper<T>) action.wrapper;
if (action.type == Action.Type.ADD) {
// remove then add in case the element already exists
added.remove(wrapper);
added.add(wrapper);
} else if (action.type == Action.Type.REMOVE) {
added.remove(wrapper);
}
}
return added;
}
public <T> Collection<ModelWrapper<T>> getRemoved(
Property<? extends Collection<? extends T>, ? super E> property) {
Collection<ModelWrapper<T>> removed = new ArrayList<ModelWrapper<T>>();
List<Action> actions = getActions(property);
for (Action action : actions) {
@SuppressWarnings("unchecked")
ModelWrapper<T> wrapper = (ModelWrapper<T>) action.wrapper;
if (action.type == Action.Type.ADD) {
removed.remove(wrapper);
} else if (action.type == Action.Type.REMOVE) {
// remove then add in case the element already exists
removed.remove(wrapper);
removed.add(wrapper);
}
}
return removed;
}
/**
* Clear all queues.
*/
public void clear() {
map.clear();
}
private List<Action> getActions(Property<?, ? super E> property) {
List<Action> queue = map.get(property);
if (queue == null) {
queue = new ArrayList<Action>();
map.put(property, queue);
}
return queue;
}
public static class Action {
public enum Type {
ADD, REMOVE;
}
public final Type type;
public final ModelWrapper<?> wrapper;
public Action(Type type, ModelWrapper<?> wrapper) {
this.type = type;
this.wrapper = wrapper;
}
}
}