package fr.openwide.core.wicket.more.model;
import java.io.Serializable;
import java.util.Collection;
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.commons.util.functional.Suppliers2;
import fr.openwide.core.wicket.more.markup.repeater.collection.ICollectionModel;
import fr.openwide.core.wicket.more.util.model.Models;
/**
* A {@link ICollectionModel} whose content is to be "cloned" (i.e. copied to a new
* collection) each time {@link #setObject(Collection)} is called.
*
* <p>This is typically what you want when editing a collection in a form.
*
* <p>Instances of this class are guaranteed to always return the same model for a given element, up to this element's
* removal from the collection.
*
* <p><strong>WARNING:</strong> this model is only intended to contain small collections. It is absolutely not optimized
* for large collections (say, more than just one or two dozens of items). Performance issues may arise when dealing
* with large collections.
*
* @see AbstractCollectionCopyModel
*/
public final class CollectionCopyModel<T, C extends Collection<T>, M extends IModel<T>>
extends AbstractCollectionCopyModel<T, C, M> {
private static final long serialVersionUID = -1768835911782930879L;
private final Supplier<? extends C> newCollectionSupplier;
private final Function<? super T, ? extends M> itemModelFunction;
/**
* Creates a collection copy model suitable for elements that may be safely serialized as is, such as enums.
* <p>This <strong>should not</strong> be used when your elements are database entities.
* <p><strong>Be aware</strong> that you probably won't need to implement the supplier yourself,
* as {@link Suppliers2} provides a wide range of collection suppliers.
*/
public static <T extends Serializable, C extends Collection<T>, M extends IModel<T>> CollectionCopyModel<T, C, Model<T>>
serializable(Supplier<? extends C> newCollectionSupplier) {
return new CollectionCopyModel<>(newCollectionSupplier, Models.<T>serializableModelFactory());
}
/**
* Creates a collection copy model suitable for elements that must be serialized through a custom model, such as entities.
* <p><strong>Be aware</strong> that you probably won't need to implement the supplier and functions yourself,
* as {@link Suppliers2} provides a wide range of collection suppliers, and several models have pre-existing
* factory functions ({@link GenericEntityModel#factory()} or {@link Models#serializableModelFactory()}, most notably).
*/
public static <T, C extends Collection<T>, M extends IModel<T>> CollectionCopyModel<T, C, M>
custom(Supplier<? extends C> newCollectionSupplier, Function<? super T, ? extends M> itemModelFunction) {
return new CollectionCopyModel<>(newCollectionSupplier, itemModelFunction);
}
/**
* @return A factory that will call {@link #custom(Supplier, Function) and put the input object in it.
*/
public static <T, C extends Collection<T>, M extends IModel<T>> Function<C, CollectionCopyModel<T, C, M>>
factory(final Supplier<? extends C> newCollectionSupplier,
final Function<? super T, ? extends M> itemModelFunction) {
return new SerializableFunction<C, CollectionCopyModel<T, C, M>>() {
private static final long serialVersionUID = 1L;
@Override
public CollectionCopyModel<T, C, M> apply(C input) {
CollectionCopyModel<T, C, M> result = custom(newCollectionSupplier, itemModelFunction);
result.setObject(input);
return result;
}
};
}
private CollectionCopyModel(Supplier<? extends C> newCollectionSupplier, Function<? super T, ? extends M> itemModelFunction) {
super();
this.newCollectionSupplier = newCollectionSupplier;
this.itemModelFunction = itemModelFunction;
setObject(null); // Sets to an empty collection
}
@Override
protected C createCollection() {
return newCollectionSupplier.get();
}
@Override
protected M createModel(T item) {
return itemModelFunction.apply(item);
}
}