package pt.ist.vaadinframework.data; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import pt.ist.fenixframework.Atomic; import pt.ist.vaadinframework.data.util.ServiceUtils; import com.vaadin.data.BufferedValidatable; import com.vaadin.data.Property; import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.ObjectProperty; public class BufferedProperty<Type> extends AbstractHintedProperty<Type> implements BufferedValidatable { protected Type cache; protected final Property wrapped; protected boolean modified = false; private boolean writeThrough = false; private boolean readThrough = false; private boolean invalidAllowed = true; private boolean invalidCommited = false; private List<Validator> validators; public BufferedProperty(Property wrapped, Hint... hints) { super(hints); this.wrapped = wrapped; this.cache = convertValue(wrapped.getValue()); } public BufferedProperty(Class<? extends Type> type, Hint... hints) { super(hints); this.wrapped = new ObjectProperty<Type>(null, (Class<Type>) type); this.cache = convertValue(null); } public BufferedProperty(Type value, Hint... hints) { super(hints); this.wrapped = new ObjectProperty<Type>(value); this.cache = convertValue(value); } public BufferedProperty(Type value, Class<? extends Type> type, Hint... hints) { super(hints); this.wrapped = new ObjectProperty<Type>(value, (Class<Type>) type); this.cache = convertValue(value); } /** * Override to enable property conversion on cache update. * * @param value * Value read from the wrapped property. * @return Converted value to submit to the cache. */ protected Type convertValue(Object value) throws ConversionException { return (Type) value; } @Override public Type getValue() { if (isReadThrough() && !isModified()) { cache = convertValue(wrapped.getValue()); } return cache; } @Override public void setValue(Object newValue) throws ReadOnlyException, ConversionException { if (isReadOnly()) { throw new ReadOnlyException(); } // If invalid values are not allowed, the value must be checked if (!isInvalidAllowed()) { final Collection<Validator> v = getValidators(); if (v != null) { for (Validator validator : v) { validator.validate(newValue); } } } Type current = convertValue(wrapped.getValue()); Type prevCache = cache; cache = convertValue(newValue); processNewCacheValue(); if (differ(current, cache)) { modified = true; if (isWriteThrough()) { commit(); } } else { modified = false; } if (differ(prevCache, cache)) { fireValueChange(); } } protected void processNewCacheValue() { } protected boolean differ(Object oldV, Object newV) { if (newV != null) { return !newV.equals(oldV); } if (oldV != null) { return true; } return false; } @Override public Class<? extends Type> getType() { if (getValue() != null) { return (Class<? extends Type>) getValue().getClass(); } return (Class<? extends Type>) wrapped.getType(); } /** * @see com.vaadin.data.Buffered#commit() */ @Atomic @Override public void commit() throws SourceException, InvalidValueException { try { if (!isInvalidCommitted() && !isValid()) { validate(); } if (isModified()) { wrapped.setValue(cache); } modified = false; } catch (Throwable e) { ServiceUtils.handleException(e); throw new SourceException(BufferedProperty.this, e); } } /** * @see com.vaadin.data.Buffered#discard() */ @Override public void discard() throws SourceException { Type prevCache = cache; cache = convertValue(wrapped.getValue()); if (differ(prevCache, cache)) { fireValueChange(); } modified = false; } /** * @see com.vaadin.data.Buffered#isWriteThrough() */ @Override public boolean isWriteThrough() { return writeThrough; } /** * @see com.vaadin.data.Buffered#setWriteThrough(boolean) */ @Override public void setWriteThrough(boolean writeThrough) throws SourceException, InvalidValueException { if (writeThrough != this.writeThrough) { this.writeThrough = writeThrough; if (writeThrough && modified) { commit(); } } } /** * @see com.vaadin.data.Buffered#isReadThrough() */ @Override public boolean isReadThrough() { return readThrough; } /** * @see com.vaadin.data.Buffered#setReadThrough(boolean) */ @Override public void setReadThrough(boolean readThrough) throws SourceException { if (readThrough != this.readThrough) { this.readThrough = readThrough; } } /** * @see com.vaadin.data.Buffered#isModified() */ @Override public boolean isModified() { return modified; } /** * @see com.vaadin.data.Validatable#addValidator(com.vaadin.data.Validator) */ @Override public void addValidator(Validator validator) { if (validators == null) { validators = new LinkedList<Validator>(); } validators.add(validator); } /** * @see com.vaadin.data.Validatable#removeValidator(com.vaadin.data.Validator) */ @Override public void removeValidator(Validator validator) { if (validators != null) { validators.remove(validator); } } /** * @see com.vaadin.data.Validatable#getValidators() */ @Override public Collection<Validator> getValidators() { if (validators == null || validators.isEmpty()) { return null; } return Collections.unmodifiableCollection(validators); } /** * @see com.vaadin.data.Validatable#isValid() */ @Override public boolean isValid() { if (validators != null) { for (Validator validator : validators) { if (!validator.isValid(this.getValue())) { return false; } } } return true; } /** * @see com.vaadin.data.Validatable#validate() */ @Override public void validate() throws InvalidValueException { LinkedList<InvalidValueException> errors = null; if (validators != null) { for (Validator validator : validators) { try { validator.validate(this.getValue()); } catch (InvalidValueException e) { if (errors == null) { errors = new LinkedList<InvalidValueException>(); } errors.add(e); } } } if (errors != null) { throw new InvalidValueException(null, errors.toArray(new InvalidValueException[0])); } } /** * @see com.vaadin.data.Validatable#isInvalidAllowed() */ @Override public boolean isInvalidAllowed() { return invalidAllowed; } /** * @see com.vaadin.data.Validatable#setInvalidAllowed(boolean) */ @Override public void setInvalidAllowed(boolean invalidAllowed) throws UnsupportedOperationException { this.invalidAllowed = invalidAllowed; } /** * @see com.vaadin.data.BufferedValidatable#isInvalidCommitted() */ @Override public boolean isInvalidCommitted() { return invalidCommited; } /** * @see com.vaadin.data.BufferedValidatable#setInvalidCommitted(boolean) */ @Override public void setInvalidCommitted(boolean invalidCommitted) { this.invalidCommited = invalidCommitted; } }