com.nitorcreations.wicket.model.GroupingMultimapModel Maven / Gradle / Ivy
package com.nitorcreations.wicket.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultimap;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.lang.Args;
import ch.lambdaj.function.argument.Argument;
/**
* A model that groups a list of items by a Lambdaj argument specified by {@link #getKeyArgument()}.
*
* The lists will be kept in order by specifying {@link com.google.common.collect.Ordering} for
* keys and values in {@link #getKeyOrdering()} and {@link #getValueOrdering()}, correspondingly.
*
* WARNING: The used {@link com.google.common.collect.Ordering}s must be in accordance with the
* {@code equals} and {@code hashCode} of the keys and values.
*
* Example
*
* Team:
* name: String
* Player:
* name: String
* team: Team
*
* A list of players could be grouped using {@code argument(on(Player.class).getTeam())}:
*
* Team: Dallas Stars
* - Player: Jaromir Jagr
* - Player: Derek Roy
* Team: Anaheim Ducks
* - Player: Teemu Selänne
* - Player: Ryan Getzlaf
*
* @param the type of the key
* @param the type of a single item in the original list and values list
*/
public abstract class GroupingMultimapModel implements GroupingModel {
private static final long serialVersionUID = 5096283540351480281L;
private final IModel> model;
private transient TreeMultimap map;
@SuppressWarnings("unchecked")
public GroupingMultimapModel(IModel extends Collection extends V>> model) {
this.model = (IModel>) Args.notNull(model, "Model");
}
@Override
public void detach() {
model.detach();
map = null;
}
/**
* @return the argument path to group the items by
*/
protected abstract Argument getKeyArgument();
/**
* Get the ordering for the keys specified by {@link #getKeyArgument()}
*
* Warning: The comparators or comparables used must be consistent
* with equals as explained by the {@link Comparable} class specification.
* Otherwise, the resulting multiset will violate the general contract of {@link
* com.google.common.collect.SetMultimap}, which it is specified in terms of {@link Object#equals}.
*
* @return the ordering for the values
*/
protected abstract Ordering getKeyOrdering();
/**
* Get the ordering for the values.
*
* Warning: The comparators or comparables used must be consistent
* with equals as explained by the {@link Comparable} class specification.
* Otherwise, the resulting multiset will violate the general contract of {@link
* com.google.common.collect.SetMultimap}, which it is specified in terms of {@link Object#equals}.
*
* @return the ordering for the values
*/
protected abstract Ordering getValueOrdering();
protected TreeMultimap getMap() {
if (map == null) {
final Argument keyArgument = getKeyArgument();
map = TreeMultimap.create(getKeyOrdering(), getValueOrdering());
for (V original : model.getObject()) {
map.put(keyArgument.evaluate(original), original);
}
}
return map;
}
@Override
public IModel> getKeysModel() {
return new AbstractReadOnlyModel>() {
private static final long serialVersionUID = 4243834867222027035L;
@Override
public List getObject() {
return new ArrayList(getMap().keySet());
}
};
}
@Override
public IModel> getValuesModel(final IModel key) {
return new ValuesModel(key);
}
@Override
public IModel> getWrappedModel() {
return model;
}
@Override
public Collection getObject() {
return model.getObject();
}
@Override
public void setObject(Collection object) {
model.getObject().clear();
model.getObject().addAll(object);
this.detach();
}
private class ValuesModel implements IModel> {
private static final long serialVersionUID = 4243834867222027035L;
private final IModel key;
public ValuesModel(IModel key) {
this.key = key;
}
@Override
public List getObject() {
return new ArrayList(getMap().get(key.getObject()));
}
@Override
public void setObject(List object) {
final TreeMultimap multimap = getMap();
final K keyObject = key.getObject();
multimap.removeAll(keyObject);
multimap.putAll(keyObject, object);
GroupingMultimapModel.this.setObject(multimap.values());
}
@Override
public void detach() {
key.detach();
}
}
}