package fr.lteconsulting.hexa.databinding;
import java.util.logging.Logger;
import fr.lteconsulting.hexa.client.tools.Action2;
import fr.lteconsulting.hexa.databinding.propertyadapters.ObjectPropertyAdapter;
import fr.lteconsulting.hexa.databinding.propertyadapters.PropertyAdapter;
/**
* Manages the binding between a source and a destination.
*
* <p>
* The data binding has several options like OneWay, TwoWay, ...<br/>
* The data propagation can happen synchronously after a data changed, or it can
* happen asynchronously through a deferred command.
*
* @author Arnaud Tournier
*
*/
public class DataBinding
{
private static final Logger LOGGER = Logger.getLogger( DataBinding.class.getName() );
private boolean fActivated;
private PropertyAdapter source;
private Object sourceHandler;
private boolean fSettingSource;
private PropertyAdapter destination;
private Object destinationHandler;
private boolean fSettingDestination;
private Converter converter;
private final String logPrefix;
public DataBinding( Object source, String sourceProperty, Object destination, String destinationProperty, Mode bindingMode )
{
this( new ObjectPropertyAdapter( source, sourceProperty ), new ObjectPropertyAdapter( destination, destinationProperty ), bindingMode, null, null );
}
public DataBinding( PropertyAdapter source, PropertyAdapter destination, Mode bindingMode, Converter converter, String logPrefix )
{
this.source = source;
this.destination = destination;
this.converter = converter;
this.logPrefix = logPrefix;
switch( bindingMode )
{
case OneWay:
sourceHandler = source.registerPropertyChanged( onSourceChanged, null );
break;
case OneWayToSource:
destinationHandler = destination.registerPropertyChanged( onDestinationChanged, null );
break;
case TwoWay:
sourceHandler = source.registerPropertyChanged( onSourceChanged, null );
destinationHandler = destination.registerPropertyChanged( onDestinationChanged, null );
break;
}
}
/**
* Activates the data binding and propagates the source to the destination
*/
public DataBinding activate()
{
fActivated = true;
log( "activation" );
onSourceChanged.exec( null, null );
return this;
}
/**
* Suspend the data binding. Can be reactivated with {@link activate}
*/
public DataBinding suspend()
{
fActivated = false;
log( "suspended" );
return this;
}
/**
* Terminates the Data Binding activation and cleans up all related
* resources. You should call this method when you want to free the binding,
* in order to lower memory usage.
*/
public void terminate()
{
log( "term" );
fActivated = false;
converter = null;
source.removePropertyChangedHandler( sourceHandler );
source = null;
sourceHandler = null;
destination.removePropertyChangedHandler( destinationHandler );
destination = null;
destinationHandler = null;
}
protected void log( String text )
{
if( logPrefix == null )
return;
LOGGER.info( "DATABINDING " + logPrefix + " : " + text );
}
private final Action2<PropertyAdapter, Object> onSourceChanged = new Action2<PropertyAdapter, Object>()
{
@Override
public void exec( PropertyAdapter param, Object cookie )
{
// prevent us to wake up ourselves
if( fSettingSource )
return;
if( logPrefix != null )
log( "source changed, propagating to destination ..." );
if( !fActivated )
return;
Object value = source.getValue();
if( logPrefix != null )
log(" - source value : " + value);
if( converter != null )
{
if( logPrefix != null )
log( "... converting value ..." );
value = converter.convert( value );
if( logPrefix != null )
log(" - converted to : " + value);
}
fSettingDestination = true;
destination.setValue( value );
fSettingDestination = false;
if( logPrefix != null )
log( " - done propagating source" );
}
};
private final Action2<PropertyAdapter, Object> onDestinationChanged = new Action2<PropertyAdapter, Object>()
{
@Override
public void exec( PropertyAdapter param, Object cookie )
{
// prevent us to wake up ourselves
if( fSettingDestination )
return;
if( !fActivated )
return;
log( "destination changed, propagating to source ..." );
Object value = destination.getValue();
if( converter != null )
{
log( "... converting value ..." );
value = converter.convertBack( value );
}
fSettingSource = true;
source.setValue( value );
fSettingSource = false;
log( "done setting destination to " + value );
}
};
}