com.dragome.forms.bindings.client.bean.BeanPropertyValueModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dragome-form-bindings Show documentation
Show all versions of dragome-form-bindings Show documentation
Dragome SDK module: form bindings
/*
* Copyright 2009 Andrew Pietsch
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package com.dragome.forms.bindings.client.bean;
import com.dragome.forms.bindings.client.value.AbstractMutableValueModel;
import com.dragome.forms.bindings.client.value.ValueHolder;
import com.dragome.forms.bindings.client.value.ValueModel;
import com.dragome.model.interfaces.ValueChangeEvent;
import com.dragome.model.interfaces.ValueChangeHandler;
/**
*
*/
public class BeanPropertyValueModel extends AbstractMutableValueModel implements BeanPropertyModelBase, HasMutableModel
{
private PropertyDescriptor propertyDescriptor;
private T checkpointValue;
private T currentValue;
private ValueHolder dirtyModel= new ValueHolder(false);
private ValueHolder mutableModel= new ValueHolder(false);
private ValueModel> source;
private ValueModel autoCommit;
private UpdateStrategy defaultUpdateStrategy= new DefaultUpdateStrategy();
private UpdateStrategy autoCommitUpdateStrategy= new AutoCommitUpdateStrategy();
public BeanPropertyValueModel(ValueModel> sourceModel, PropertyDescriptor descriptor, ValueModel autoCommit)
{
this.source= sourceModel;
this.propertyDescriptor= descriptor;
this.autoCommit= autoCommit;
dirtyModel.setFireEventsEvenWhenValuesEqual(false);
installValueChangeHandler(source);
handleSourceModelChange();
}
@SuppressWarnings("unchecked")
private void installValueChangeHandler(ValueModel> source)
{
// yep I know, no generics... I don't know or care what the type is since the generated accessor handles
// all that. We could get around this if ValueChangeHandler allowed for ValueChangeHandler super T>.
source.addValueChangeHandler(new ValueChangeHandler()
{
public void onValueChange(ValueChangeEvent event)
{
handleSourceModelChange();
}
});
}
private void handleSourceModelChange()
{
readFromSource();
onSourceModelChanged(source.getValue());
}
/**
* This is an empty method that subclasses can override to perform
* actions when the source bean changes.
*
* @param sourceBean the new value of the source bean.
*/
protected void onSourceModelChanged(Object sourceBean)
{
}
public String getPropertyName()
{
return propertyDescriptor.getPropertyName();
}
public Class getValueType()
{
return propertyDescriptor.getValueType();
}
protected boolean isAutoCommit()
{
// only true if not null and true.
return Boolean.TRUE.equals(autoCommit.getValue());
}
private void ensureMutable()
{
if (!isMutableProperty())
{
throw new ReadOnlyPropertyException(propertyDescriptor);
}
else if (!isNonNullSource())
{
throw new SourceBeanIsNullException(propertyDescriptor);
}
}
public void setValue(T value)
{
getUpdateStrategy().setValue(value);
}
public T getValue()
{
return getUpdateStrategy().getValue();
}
public void writeToSource(boolean checkpoint)
{
getUpdateStrategy().writeToSource(checkpoint);
}
public void readFromSource()
{
getUpdateStrategy().readFromSource();
}
/**
* Checkpoints the models dirty state to the current value of the model. After calling this
* method the dirty state will be false
.
*
* @see revertToCheckpoint()
*/
public void checkpoint()
{
getUpdateStrategy().checkpoint();
}
/**
* Reverts the value of this model to the previous checkpoint. If checkpoint hasn't been called
* then it will revert to the last call to readFrom.
*/
public void revert()
{
getUpdateStrategy().revertToCheckpoint();
}
public ValueModel mutable()
{
return mutableModel;
}
public boolean isMutable()
{
return mutable().getValue();
}
private void updateMutableState()
{
mutableModel.setValue(isMutableProperty() && isNonNullSource());
}
private boolean isNonNullSource()
{
return source.getValue() != null;
}
public boolean isMutableProperty()
{
return propertyDescriptor.isMutable();
}
/**
* @deprecated use {@link #dirty()} instead.
*/
@Deprecated
public ValueModel getDirtyModel()
{
return dirty();
}
public ValueModel dirty()
{
return dirtyModel;
}
protected UpdateStrategy getUpdateStrategy()
{
return isAutoCommit() ? autoCommitUpdateStrategy : defaultUpdateStrategy;
}
private interface UpdateStrategy
{
void readFromSource();
void writeToSource(boolean checkpoint);
void setValue(T value);
void checkpoint();
void revertToCheckpoint();
T getValue();
}
private class DefaultUpdateStrategy implements UpdateStrategy
{
@SuppressWarnings("unchecked")
public void readFromSource()
{
checkpointValue= (T) propertyDescriptor.readProperty(source.getValue());
updateMutableState();
setValueInternal(checkpointValue);
}
public void writeToSource(boolean checkpoint)
{
ensureMutable();
T value= getValue();
propertyDescriptor.writeProperty(source.getValue(), value);
if (checkpoint)
{
checkpoint();
}
}
public void setValue(T value)
{
ensureMutable();
setValueInternal(value);
}
public T getValue()
{
return currentValue;
}
public void checkpoint()
{
checkpointValue= getValue();
dirtyModel.setValue(false);
}
public void revertToCheckpoint()
{
setValueInternal(checkpointValue);
}
protected void setValueInternal(T value)
{
T oldValue= currentValue;
currentValue= value;
fireValueChangeEvent(oldValue, value);
afterMutate();
}
protected void afterMutate()
{
updateDirtyState();
}
void updateDirtyState()
{
dirtyModel.setValue(computeDirty());
}
boolean computeDirty()
{
return currentValue == null ? checkpointValue != null : !currentValue.equals(checkpointValue);
}
}
private class AutoCommitUpdateStrategy extends DefaultUpdateStrategy
{
boolean inReadFromSource= false;
@Override
public void readFromSource()
{
// this is a bit of a hack, but we flag that we're inside a
// readFromSource call so that we can re-use setValueInternal without
// causing a write back to the source.
boolean oldInReadFromSource= inReadFromSource;
inReadFromSource= true;
try
{
super.readFromSource();
}
finally
{
inReadFromSource= oldInReadFromSource;
}
}
@Override
protected void afterMutate()
{
// We don't write if we're currently reading from the source;
if (!inReadFromSource)
{
// we don't checkpoint so the user can still checkpoint and revert
// even though were writing through to the bean.
writeToSource(false);
}
}
@Override
boolean computeDirty()
{
// we're never dirty...
return false;
}
}
}