package au.com.vaadinutils.ui;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicReference;
import javax.validation.ConstraintViolationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.vaadin.teemu.wizards.WizardStep;
import com.google.common.base.Preconditions;
import com.vaadin.addon.jpacontainer.EntityItem;
import com.vaadin.addon.jpacontainer.JPAContainer;
import com.vaadin.addon.jpacontainer.JPAContainer.ProviderChangedEvent;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
import com.vaadin.ui.Component;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import au.com.vaadinutils.crud.CrudEntity;
import au.com.vaadinutils.crud.FormHelper;
import au.com.vaadinutils.crud.ValidatingFieldGroup;
import au.com.vaadinutils.dao.EntityManagerProvider;
import au.com.vaadinutils.dao.JpaBaseDao;
import au.com.vaadinutils.util.VUNotification;
public abstract class SingleEntityWizardStep<E extends CrudEntity> implements WizardStep
{
private static Logger logger = LogManager.getLogger(SingleEntityWizardStep.class);
private au.com.vaadinutils.crud.ValidatingFieldGroup<E> fieldGroup;
private JPAContainer<E> container;
private E entity;
private boolean isNew = false;
private Class<E> entityClass;
private Component editor = null;
public SingleEntityWizardStep(JpaBaseDao<E, Long> entityDao, Class<E> entityClass)
{
this.entityClass = entityClass;
this.container = entityDao.createVaadinContainer();
fieldGroup = new ValidatingFieldGroup<>(entityClass);
}
@Override
public abstract String getCaption();
@SuppressWarnings("unchecked")
@Override
public Component getContent()
{
// if (editor == null)
{
this.entity = findEntity();
EntityItem<E> entityItem;
if (entity == null)
{
try
{
isNew = true;
entity = entityClass.newInstance();
initEntity(entity);
entityItem = container.createEntityItem(entity);
}
catch (InstantiationException e)
{
logger.error(e, e);
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
logger.error(e, e);
throw new RuntimeException(e);
}
}
else
{
isNew = false;
Long itemId = entity.getId();
entityItem = container.getItem(itemId);
// As we did a lookup the entity we retrieved during the lookup
// may not be
// the one we retrieve from the container.
entity = entityItem.getEntity();
}
Preconditions.checkNotNull(entityItem);
Preconditions.checkArgument(entity == entityItem.getEntity());
fieldGroup.setItemDataSource(entityItem);
editor = getContent(fieldGroup);
}
Preconditions.checkArgument(((EntityItem<E>) fieldGroup.getItemDataSource()).getEntity() == entity);
return editor;
}
/**
* Do any custom initialisation of a new entity.
*
* @param entity
*/
abstract protected void initEntity(E entity);
/**
*
* Search for an existing entity to edit or return null if one doesn't
* exist.
*
* @return
*/
abstract protected E findEntity();
/**
* Build the layout used for editing the entity Any fields must be bound to
* the field Group.
*
* @param fieldGroup
* @return
*/
abstract protected Component getContent(ValidatingFieldGroup<E> fieldGroup);
@Override
public boolean onAdvance()
{
return validate();
}
protected boolean validate()
{
boolean valid = false;
try
{
if (!fieldGroup.isValid())
{
Notification.show("Validation Errors", "Please fix any field errors and try again.",
Type.WARNING_MESSAGE);
}
else
{
fieldGroup.commit();
if (isNew)
{
Object id = container.addEntity(entity);
EntityItem<E> entityItem = container.getItem(id);
entity = entityItem.getEntity();
fieldGroup.setItemDataSource(entityItem);
isNew = false;
}
entity = commitContainerAndGetEntityFromDB();
// container.commit();
// entity = container.getItem(entity.getId()).getEntity();
valid = true;
VUNotification.show("The details have been saved.", Type.TRAY_NOTIFICATION);
}
}
catch (ConstraintViolationException e)
{
FormHelper.showConstraintViolation(e);
}
catch (CommitException e)
{
logger.error(e, e);
if (e.getCause() instanceof ConstraintViolationException)
FormHelper.showConstraintViolation(((ConstraintViolationException) e.getCause()));
else
Notification.show(e.getMessage(), Type.ERROR_MESSAGE);
}
return valid;
}
@Override
public boolean onBack()
{
return true;
}
public E getEntity()
{
return this.entity;
}
protected ValidatingFieldGroup<E> getFieldGroup()
{
return fieldGroup;
}
/**
* commits the container and retrieves the new recordid
*
* we have to hook the ItemSetChangeListener to be able to get the database
* id of a new record.
*/
private E commitContainerAndGetEntityFromDB()
{
// don't really need an AtomicReference, just using it as a mutable
// final variable to be used in the callback
final AtomicReference<E> newEntity = new AtomicReference<>();
// call back to collect the id of the new record when the container
// fires the ItemSetChangeEvent
ItemSetChangeListener tmp = new ItemSetChangeListener()
{
/**
*
*/
private static final long serialVersionUID = 9132090066374531277L;
@Override
public void containerItemSetChange(ItemSetChangeEvent event)
{
if (event instanceof ProviderChangedEvent)
{
@SuppressWarnings("rawtypes")
ProviderChangedEvent pce = (ProviderChangedEvent) event;
@SuppressWarnings("unchecked")
Collection<E> affectedEntities = pce.getChangeEvent().getAffectedEntities();
if (affectedEntities.size() > 0)
{
@SuppressWarnings("unchecked")
E id = (E) affectedEntities.toArray()[0];
newEntity.set(id);
}
}
}
};
try
{
// add the listener
container.addItemSetChangeListener(tmp);
// call commit
container.commit();
newEntity.set(EntityManagerProvider.getEntityManager().merge(newEntity.get()));
}
catch (com.vaadin.data.Buffered.SourceException e)
{
if (e.getCause() instanceof javax.persistence.PersistenceException)
{
javax.persistence.PersistenceException cause = (javax.persistence.PersistenceException) e.getCause();
Notification.show(cause.getCause().getMessage(), Type.ERROR_MESSAGE);
}
}
finally
{
// detach the listener
container.removeItemSetChangeListener(tmp);
}
// return the entity
return newEntity.get();
}
}