package au.com.vaadinutils.crud;
import java.util.HashSet;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.vaadin.addon.jpacontainer.JPAContainer;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.validator.BeanValidator;
import com.vaadin.ui.Field;
public class ValidatingFieldGroup<E> extends FieldGroup
{
private static final long serialVersionUID = 1L;
private final Class<E> entityClass;
private JPAContainer<E> container;
Logger logger = LogManager.getLogger();
public ValidatingFieldGroup(JPAContainer<E> container, Class<E> entityClass)
{
this.entityClass = entityClass;
this.container = container;
}
public ValidatingFieldGroup(Item item, Class<E> entityClass)
{
super(item);
this.entityClass = entityClass;
}
public ValidatingFieldGroup(Class<E> entityClass)
{
this.entityClass = entityClass;
}
private boolean groupIsDirty;
private DirtyListener dirtyListener;
public DirtyListener getDirtyListener()
{
return dirtyListener;
}
public void setDirtyListener(DirtyListener listener)
{
dirtyListener = listener;
}
public boolean getGroupIsDirty()
{
return groupIsDirty;
}
public void setGroupIsDirty(boolean groupIsDirty)
{
this.groupIsDirty = groupIsDirty;
}
private Set<Field<?>> knownFields = new HashSet<Field<?>>();
/*
* Override configureField to add a bean validator to each field.
*/
@Override
protected void configureField(Field<?> field)
{
// Vaadin applies the readonly status from the underlying entity
// which doesn't allow us to make a single field readonly
// hence we track it ourselves.
boolean readOnly = field.isReadOnly();
super.configureField(field);
// If the field was originally readonly then force it back to readonly.
if (readOnly)
field.setReadOnly(true);
if (!knownFields.contains(field))
{
// only ever add the validator once for a field
// Add Bean validators if there are annotations
// Note that this requires a bean validation implementation to
// be available.
BeanValidator validator = new BeanValidator(entityClass, getPropertyId(field).toString());
field.addValidator(validator);
if (field.getLocale() != null)
{
validator.setLocale(field.getLocale());
}
ValueChangeListener changeListener = new ValueChangeListener()
{
private static final long serialVersionUID = 1L;
@Override
public void valueChange(ValueChangeEvent event)
{
if (groupIsDirty == false)
{
groupIsDirty = true;
if (dirtyListener != null)
{
dirtyListener.fieldGroupIsDirty(true);
}
}
}
};
field.addValueChangeListener(changeListener);
}
knownFields.add(field);
}
public void discard()
{
groupIsDirty = false;
if (dirtyListener != null)
{
dirtyListener.fieldGroupIsDirty(false);
}
super.discard();
}
public void setItemDataSource(Item itemDataSource)
{
groupIsDirty = false;
if (dirtyListener != null)
{
dirtyListener.fieldGroupIsDirty(false);
}
super.setItemDataSource(itemDataSource);
}
public JPAContainer<E> getContainer()
{
return container;
}
// when finished testing delete this method, it's in the super type anyway
@Override
public boolean isModified()
{
for (Field<?> field : getFields())
{
if (field.isModified())
{
logger.warn("Dirty: {} {}", field.getCaption(), field.getClass().getSimpleName());
logger.warn("Dirty value: " + field.getValue());
return true;
}
}
return false;
}
public void bind(Field<?> field, Object propertyId) throws BindException
{
boolean isReadonly = false;
try
{
isReadonly = field.isReadOnly();
field.setReadOnly(false);
super.bind(field, propertyId);
}
finally
{
field.setReadOnly(isReadonly);
}
}
}