package fr.openwide.core.wicket.more.model; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.wicket.Component; import org.apache.wicket.model.IComponentAssignedModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.IWrapModel; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import fr.openwide.core.commons.util.functional.SerializableFunction; import fr.openwide.core.wicket.more.markup.repeater.collection.ICollectionModel; import fr.openwide.core.wicket.more.markup.repeater.map.IMapModel; import fr.openwide.core.wicket.more.util.model.Models; public class ReadOnlyMapModel<K, V, M extends Map<K, V>> implements IMapModel<K, V, M>, IComponentAssignedModel<M> { private static final long serialVersionUID = -6272545665317639093L; private final IModel<? extends M> readModel; private final Function<? super K, ? extends IModel<K>> keyModelFactory; public static <K, V, M extends Map<K, V>> ReadOnlyMapModel<K, V, M> of( IModel<? extends M> model, Function<? super K, ? extends IModel<K>> keyFactory) { return new ReadOnlyMapModel<K, V, M>(model, keyFactory); } protected ReadOnlyMapModel(IModel<? extends M> readModel, Function<? super K, ? extends IModel<K>> keyModelFactory) { super(); this.readModel = checkNotNull(readModel); this.keyModelFactory = checkNotNull(keyModelFactory); } @Override public M getObject() { return readModel.getObject(); } @Override public void setObject(M object) { throw newReadOnlyException(); } private UnsupportedOperationException newReadOnlyException() { return new UnsupportedOperationException("Model " + getClass() + " is read-only"); } @Override public void detach() { readModel.detach(); } @Override public ICollectionModel<K, Set<K>> keysModel() { return new KeysModel(); } protected class KeysModel extends AbstractMapCollectionModel<K, Set<K>, IModel<K>> { private static final long serialVersionUID = 1L; @Override public void detach() { ReadOnlyMapModel.this.detach(); } @Override public Set<K> getObject() { Map<K, ?> map = ReadOnlyMapModel.this.getObject(); return map == null ? null : map.keySet(); } @Override protected Iterable<IModel<K>> internalIterable() { return keyModelIterable(); } } private Iterable<IModel<K>> keyModelIterable() { M map = readModel.getObject(); if (map == null) { return ImmutableSet.<IModel<K>>of(); } else { return Iterables.transform(map.keySet(), keyModelFactory); } } @Override public ICollectionModel<V, Collection<V>> valuesModel() { return new ValuesModel(); } protected class ValuesModel extends AbstractMapCollectionModel<V, Collection<V>, IModel<V>> { private static final long serialVersionUID = 1L; @Override public void detach() { ReadOnlyMapModel.this.detach(); } @Override public Collection<V> getObject() { Map<?, V> map = ReadOnlyMapModel.this.getObject(); return map == null ? null : map.values(); } @Override protected Iterable<IModel<V>> internalIterable() { M map = readModel.getObject(); if (map == null) { return ImmutableSet.<IModel<V>>of(); } else { return Iterables.transform( keyModelIterable(), new SerializableFunction<IModel<K>, IModel<V>>() { private static final long serialVersionUID = 1L; @Override public IModel<V> apply(IModel<K> input) { return valueModel(input); } } ); } } } @Override public IModel<V> valueModel(IModel<? extends K> keyModel) { return Models.mapModelValueModel(this, keyModel); } @Override public IModel<V> valueModelForProvidedKeyModel(IModel<K> keyModel) { return valueModel(keyModel); } @Override public Iterator<? extends IModel<K>> iterator(long offset, long limit) { return keysModel().iterator(offset, limit); } @Override public long size() { M map = readModel.getObject(); if (map == null) { return 0L; } else { return map.size(); } } @Override public void put(K key, V value) { throw newReadOnlyException(); } @Override public void remove(K key) { throw newReadOnlyException(); } @Override public void clear() { throw newReadOnlyException(); } @Override public IWrapModel<M> wrapOnAssignment(Component component) { return new WrapModel(component); } private class WrapModel extends ReadOnlyMapModel<K, V, M> implements IWrapModel<M> { private static final long serialVersionUID = 7996314523359141428L; protected WrapModel(Component component) { super(Models.wrap(readModel, component), keyModelFactory); } @Override public IModel<?> getWrappedModel() { return ReadOnlyMapModel.this; } } }