com.dragome.forms.bindings.client.value.AbstractComputedValueModel 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.value;
import com.dragome.forms.bindings.client.util.Utils;
import com.dragome.model.interfaces.ValueChangeEvent;
import com.dragome.model.interfaces.ValueChangeHandler;
/**
*
*/
public abstract class AbstractComputedValueModel extends AbstractValueModel
{
private ValueChangeHandler changeMonitor= new ValueChangeHandler()
{
public void onValueChange(ValueChangeEvent event)
{
recompute();
}
};
private ValueModel source;
private ValueCache valueCache;
public AbstractComputedValueModel(ValueModel source)
{
if (source == null)
{
throw new NullPointerException("source is null");
}
this.source= source;
this.source.addValueChangeHandler(changeMonitor);
}
/**
* Recomputes the value and fires a value change event as required.
*/
protected void recompute()
{
// If we've never been initialised (i.e. getValue() has never been called) then
// we need to force the firing (since the `oldValue` will equal the `newValue`
// and thus no event will fire). That way any listeners that have been added
// prior to initialisation won't miss out on the first event.
// must check this before we call getCache()
boolean firstTime= !isCacheInitialised();
// if we've never been initialised then this will be the 'newValue'
T oldValue= getCache().getValue();
// now recompute and get the value.
getCache().recompute();
T newValue= getCache().getValue();
// and fire the event if the values are different or if this is the first time
// we've been asked to recompute.
if (firstTime || Utils.areDifferent(oldValue, newValue))
{
fireValueChangeEvent(newValue);
}
}
public T getValue()
{
return getCache().getValue();
}
private ValueCache getCache()
{
if (valueCache == null)
{
valueCache= new ValueCache();
}
return valueCache;
}
private boolean isCacheInitialised()
{
return valueCache != null;
}
protected abstract T computeValue(S value);
/**
* This class allows us to lazily compute the value on the first getValue() call or
* source model change event.
*
* Without the lazy computation we'd need to call recompute in our constructor... which would in
* turn case havoc for subclasses by invoking computeValue() during super()... thus before any
* fields have been initialised... at which point we'd sit back and wait for the NullPointerExceptions.
*/
private class ValueCache
{
private T value;
private ValueCache()
{
recompute();
}
public T getValue()
{
return value;
}
public void recompute()
{
value= computeValue(source.getValue());
}
}
}