package fr.openwide.core.wicket.more.model; import java.io.Serializable; import java.util.Map; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import com.google.common.base.Function; import com.google.common.base.Supplier; import fr.openwide.core.commons.util.functional.SerializableFunction; import fr.openwide.core.wicket.more.markup.repeater.map.IMapModel; import fr.openwide.core.wicket.more.util.model.Models; /** * A {@link IMapModel} whose content is to be "cloned" (i.e. copied to a new map) each time * {@link #setObject(Map)} is called. * * <p>This is typically what you want when editing a map in a form. * * <p>Instances of this class are guaranteed to always return the same model for a given element (key or value), * up to this element's removal from the map. * * <p><strong>WARNING:</strong> this model is only intended to contain small maps. It is absolutely not optimized * for large maps (say, more than just one or two dozens of items). Performance issues may arise when dealing * with large maps. * * @see AbstractMapCopyModel */ public final class MapCopyModel<K, V, M extends Map<K, V>, MK extends IModel<K>, MV extends IModel<V>> extends AbstractMapCopyModel<K, V, M, MK, MV> { private static final long serialVersionUID = 5159538764516521470L; private final Supplier<? extends M> newMapSupplier; private final Function<? super K, ? extends MK> keyModelFunction; private final Function<? super V, ? extends MV> valueModelFunction; /** * Creates a map copy model suitable for keys and values that may be safely serialized as is, such as enums. * <p>This <strong>should not</strong> be used when your elements are database entities. */ public static <K extends Serializable, V extends Serializable, M extends Map<K, V>> MapCopyModel<K, V, M, Model<K>, Model<V>> serializable(Supplier<? extends M> newMapSupplier) { return new MapCopyModel<>(newMapSupplier, Models.<K>serializableModelFactory(), Models.<V>serializableModelFactory()); } /** * Creates a map copy model suitable for keys and values that must be serialized through a custom model, such as entities. */ public static <K, V, M extends Map<K, V>, MK extends IModel<K>, MV extends IModel<V>> MapCopyModel<K, V, M, MK, MV> custom(Supplier<? extends M> newMapSupplier, Function<? super K, ? extends MK> keyModelFunction, Function<? super V, ? extends MV> valueModelFunction) { return new MapCopyModel<>(newMapSupplier, keyModelFunction, valueModelFunction); } /** * @return A factory that will call {@link #custom(Supplier, Function, Function)} and put the input object in it. */ public static <K, V, M extends Map<K, V>, MK extends IModel<K>, MV extends IModel<V>> Function<M, MapCopyModel<K, V, M, MK, MV>> factory(final Supplier<? extends M> newMapSupplier, final Function<? super K, ? extends MK> keyModelFunction, final Function<? super V, ? extends MV> valueModelFunction) { return new SerializableFunction<M, MapCopyModel<K, V, M, MK, MV>>() { private static final long serialVersionUID = 1L; @Override public MapCopyModel<K, V, M, MK, MV> apply(M input) { MapCopyModel<K, V, M, MK, MV> result = custom(newMapSupplier, keyModelFunction, valueModelFunction); result.setObject(input); return result; } }; } private MapCopyModel(Supplier<? extends M> newMapSupplier, Function<? super K, ? extends MK> keyModelFunction, Function<? super V, ? extends MV> valueModelFunction) { super(); this.newMapSupplier = newMapSupplier; this.keyModelFunction = keyModelFunction; this.valueModelFunction = valueModelFunction; } @Override protected M createMap() { return newMapSupplier.get(); } @Override protected MK createKeyModel(K key) { return keyModelFunction.apply(key); } @Override protected MV createValueModel(V value) { return valueModelFunction.apply(value); } }