package fr.openwide.core.wicket.more.model;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.injection.Injector;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import fr.openwide.core.jpa.business.generic.model.GenericEntity;
import fr.openwide.core.jpa.business.generic.service.IEntityService;
public abstract class AbstractGenericEntityCollectionModel
<K extends Serializable & Comparable<K>, E extends GenericEntity<K, ?>, C extends Collection<E>>
extends LoadableDetachableModel<C> {
private static final long serialVersionUID = -1768835911782930879L;
@SpringBean
private IEntityService entityService;
private transient boolean attached = false;
private final List<K> idList = Lists.newArrayList();
private final List<E> unsavedEntityList = Lists.newArrayList();
private final Class<E> clazz;
private final Supplier<? extends C> newCollectionSupplier;
private transient C entityCollection = null;
protected AbstractGenericEntityCollectionModel(Class<E> clazz, Supplier<? extends C> newCollectionSupplier) {
super();
Injector.get().inject(this);
this.clazz = clazz;
this.newCollectionSupplier = newCollectionSupplier;
setObject(null); // Sets to an empty collection
}
protected C createEntityCollection() {
return newCollectionSupplier.get();
}
protected K toId(E entity) {
return entity.getId();
}
protected E toEntity(K id) {
return entityService.getEntity(clazz, id);
}
@Override
protected C load() {
entityCollection = createEntityCollection();
for (int i = 0, unsavedEntityIndex = 0 ; i < idList.size() ; ++i) {
K id = idList.get(i);
if (id != null) {
entityCollection.add(toEntity(id));
} else {
assert unsavedEntityList.size() > unsavedEntityIndex;
E unsavedEntity = unsavedEntityList.get(unsavedEntityIndex);
assert unsavedEntity != null;
entityCollection.add(unsavedEntity);
++unsavedEntityIndex;
}
}
attached = true;
return entityCollection;
}
/**
* WARNING: if the client calls <code>setObject(null)</code>, a subsequent call to <code>getObject()</code>
* will not return <code>null</code>, but <em>an empty collection</em>.
*/
@Override
public void setObject(C object) {
entityCollection = createEntityCollection();
if (object != null) {
entityCollection.addAll(object);
}
attached = true;
super.setObject(entityCollection);
}
@Override
public void detach() {
if (!attached) {
fixSerializableData();
return;
}
updateSerializableData();
super.detach();
attached = false;
}
private void updateSerializableData() {
assert entityCollection != null;
// Saves the possible modifications applied to entityCollection
idList.clear();
unsavedEntityList.clear();
for (E entity : entityCollection) {
if (entity.isNew()) {
unsavedEntityList.add(entity);
idList.add(null);
} else {
// Do nothing with unsavedEntityList here (on purpose)
K id = toId(entity);
assert id != null;
idList.add(id);
}
}
entityCollection.clear();
}
/**
* If an entity has been persisted since this model has been detached, then fix the serializable data
* (this may happen if two models reference the same non-persisted entity, for instance)
*/
private void fixSerializableData() {
for (int i = 0, unsavedEntityIndex = 0 ; i < idList.size() ; ++i) {
K id = idList.get(i);
if (id == null) {
assert unsavedEntityList.size() > unsavedEntityIndex;
E unsavedEntity = unsavedEntityList.get(unsavedEntityIndex);
assert unsavedEntity != null;
if (unsavedEntity.isNew()) {
// Do nothing
++unsavedEntityIndex;
} else {
// Fix serializable data
idList.set(i, toId(unsavedEntity));
unsavedEntityList.remove(unsavedEntityIndex);
}
}
}
}
public List<K> getIdList() {
return Collections.unmodifiableList(idList);
}
}