package fr.openwide.core.wicket.more.markup.html.sort.model; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IDetachable; import org.apache.wicket.model.IModel; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import fr.openwide.core.jpa.more.business.sort.ISort; import fr.openwide.core.jpa.more.business.sort.ISort.SortOrder; import fr.openwide.core.jpa.more.business.sort.SortUtils; import fr.openwide.core.wicket.more.markup.html.sort.TableSortLink; public class CompositeSortModel<T extends ISort<?>> extends AbstractReadOnlyModel<Map<T, SortOrder>> { private static final long serialVersionUID = -3881057053212320743L; private final CompositingStrategy compositingStrategy; private final Map<T, SortOrder> map = Maps.newLinkedHashMap(); private final Map<T, SortOrder> defaultSort; private final Map<T, SortOrder> stabilizationSort; private List<IModel<? extends Map<? extends T, SortOrder>>> beforeSortModels = Lists.newArrayList(); private List<IModel<? extends Map<? extends T, SortOrder>>> beforeDefaultSortModels = Lists.newArrayList(); private List<IModel<? extends Map<? extends T, SortOrder>>> afterDefaultSortModels = Lists.newArrayList(); private List<IModel<? extends Map<? extends T, SortOrder>>> afterSortModels = Lists.newArrayList(); private static final <T extends ISort<?>> ImmutableMap<T, SortOrder> toMap(T item) { return item == null ? ImmutableMap.<T, SortOrder>of() : ImmutableMap.of(item, item.getDefaultOrder()); } public CompositeSortModel(CompositingStrategy compositingStrategy) { this(compositingStrategy, (T) null, null); } /** * @see CompositeSortModel#CompositeSortModel(CompositingStrategy, Map, Map) */ public CompositeSortModel(CompositingStrategy compositingStrategy, T defaultAndStabilizationSort) { this(compositingStrategy, defaultAndStabilizationSort, defaultAndStabilizationSort); } /** * @see CompositeSortModel#CompositeSortModel(CompositingStrategy, Map, Map) */ public CompositeSortModel(CompositingStrategy compositingStrategy, T defaultSort, T concatenatedSort) { this(compositingStrategy, toMap(defaultSort), toMap(concatenatedSort)); } /** * @param defaultSort The sort to be used when no sort was selected. This will be displayed when using {@link TableSortLink}s if no sort was selected. * @param stabilizationSort The sort to be performed after the selected sort was performed, in order to make sure the elements order will not depend * on some implementation detail. This will be ignored when using {@link TableSortLink}s. */ public CompositeSortModel(CompositingStrategy compositingStrategy, Map<T, SortOrder> defaultSort, Map<T, SortOrder> stabilizationSort) { this.compositingStrategy = compositingStrategy; this.defaultSort = ImmutableMap.copyOf(defaultSort); this.stabilizationSort = ImmutableMap.copyOf(stabilizationSort); } public CompositeSortModel<T> addBefore(IModel<? extends Map<? extends T, SortOrder>> sortModel) { beforeSortModels.add(sortModel); return this; } public CompositeSortModel<T> addBeforeDefault(IModel<? extends Map<? extends T, SortOrder>> sortModel) { beforeDefaultSortModels.add(sortModel); return this; } public CompositeSortModel<T> addAfterDefault(IModel<? extends Map<? extends T, SortOrder>> sortModel) { afterDefaultSortModels.add(sortModel); return this; } public CompositeSortModel<T> addAfter(IModel<? extends Map<? extends T, SortOrder>> sortModel) { afterSortModels.add(sortModel); return this; } private void putNewKeys(Map<T, SortOrder> map, Iterable<IModel<? extends Map<? extends T, SortOrder>>> models) { for (IModel<? extends Map<? extends T, SortOrder>> sortModel : models) { putNewKeys(map, sortModel.getObject()); } } private void putNewKeys(Map<T, SortOrder> map, Map<? extends T, SortOrder> addedMap) { SortUtils.appendTo(map, addedMap); } @Override public Map<T, SortOrder> getObject() { Map<T, SortOrder> map = new LinkedHashMap<>(); putNewKeys(map, beforeSortModels); putNewKeys(map, getActiveSort()); putNewKeys(map, afterSortModels); putNewKeys(map, stabilizationSort); return map; } /** * Returns the currently <strong>active</code> composite sort (i.e., defaults are accounted for). */ public Map<T, SortOrder> getActiveSort() { if (map.isEmpty()) { Map<T, SortOrder> map = new LinkedHashMap<>(); putNewKeys(map, beforeDefaultSortModels); putNewKeys(map, defaultSort); putNewKeys(map, afterDefaultSortModels); return map; } else { return map; } } /** * Returns the currently <strong>selected</code> composite sort (i.e., defaults are ignored). */ public Map<T, SortOrder> getSelectedSort() { return map; } /** * @deprecated Use either {@link #getActiveOrder(ISort)} or {@link #getSelectedOrder(ISort)}, depending on what you want * @param sort * @return */ @Deprecated public SortOrder getOrder(T sort) { return getActiveSort().get(sort); } /** * Returns the currently <strong>active</code> order for this sort (i.e., defaults are accounted for). * <p>Used to highlight the sort links if the sort is enabled. * We only highlight the current selection (or the default sort if there is no selection). The concatenated sort is ignored here. */ public SortOrder getActiveOrder(T sort) { return getActiveSort().get(sort); } /** * Returns the currently <strong>selected</code> order for this sort (i.e., defaults are ignored). * <p>Used to switch sort orders. */ public SortOrder getSelectedOrder(T sort) { return getSelectedSort().get(sort); } public void setOrder(T sort, SortOrder order) { compositingStrategy.setOrder(map, sort, order); } public static enum CompositingStrategy { /** * Only the last sort will be actually applied. */ LAST_ONLY { @Override protected <T extends ISort<?>> void setOrder(Map<T, SortOrder> map, T sort, SortOrder order) { if (order == null) { map.remove(sort); } else { map.clear(); map.put(sort, order); } } }, /** * Queues the sorts by order of insertion. * <p>Modifying an already active sort will leave its position in the queue unchanged. */ QUEUE_BY_INSERTION { @Override protected <T extends ISort<?>> void setOrder(Map<T, SortOrder> map, T sort, SortOrder order) { if (order == null) { map.remove(sort); } else { map.put(sort, order); } } }, /** * Queues the sorts by order of modification. * <p>Modifying an already active sort will push it to the back of the queue. */ QUEUE_BY_LAST_MODIFICATION { @Override protected <T extends ISort<?>> void setOrder(Map<T, SortOrder> map, T sort, SortOrder order) { map.remove(sort); if (order != null) { map.put(sort, order); } } }; protected abstract <T extends ISort<?>> void setOrder(Map<T, SortOrder> map, T sort, SortOrder order); } @Override public void detach() { for (IDetachable detachable : Iterables.concat(beforeSortModels, beforeDefaultSortModels, afterDefaultSortModels, afterSortModels)) { detachable.detach(); } } }