package fr.lteconsulting.hexa.databinding.propertyadapters;
import java.util.ArrayList;
import fr.lteconsulting.hexa.client.tools.Action2;
import fr.lteconsulting.hexa.databinding.PlatformSpecificProvider;
public class CompositePropertyAdapter implements PropertyAdapter
{
public final static String HASVALUE_TOKEN = "$HasValue";
public final static String DTOMAP_TOKEN = "$DTOMap";
Object context;
String[] path;
PropertyAdapter[] adapters;
Object[] adapterHandlerRegistrations;
private ArrayList<ClientInfo> clients;
public CompositePropertyAdapter( Object context, String path )
{
this.context = context;
this.path = path.split( "\\." );
adapters = new PropertyAdapter[this.path.length];
adapterHandlerRegistrations = new Object[this.path.length];
}
private Action2<PropertyAdapter, Object> onPropertyChanged = new Action2<PropertyAdapter, Object>()
{
@Override
public void exec( PropertyAdapter p1, Object p2 )
{
int adapterNo = (Integer) p2;
// unregister all adapters with a position > adapterNo
for( int p = adapterNo + 1; p < path.length; p++ )
{
if( adapters[p] == null || adapterHandlerRegistrations[p] == null )
continue;
adapters[p].removePropertyChangedHandler( adapterHandlerRegistrations[p] );
adapters[p] = null;
adapterHandlerRegistrations[p] = null;
}
// signal callbacks that a change occured
if( clients != null )
{
for( ClientInfo client : clients )
client.callback.exec( CompositePropertyAdapter.this, client.cookie );
}
}
};
@Override
public Object getValue()
{
tryCreateAdapters();
if( adapters[path.length - 1] != null )
{
return adapters[path.length - 1].getValue();
}
return null;
}
@Override
public void setValue( Object object )
{
tryCreateAdapters();
if( adapters[path.length - 1] != null )
adapters[path.length - 1].setValue( object );
}
class ClientInfo
{
Action2<PropertyAdapter, Object> callback;
Object cookie;
}
@Override
public Object registerPropertyChanged( Action2<PropertyAdapter, Object> callback, Object cookie )
{
tryCreateAdapters();
if( clients == null )
clients = new ArrayList<ClientInfo>();
ClientInfo client = new ClientInfo();
client.callback = callback;
client.cookie = cookie;
// what if any sub path contains a property that's not subscribable ?
clients.add( client );
return client;
}
@Override
public void removePropertyChangedHandler( Object handlerRegistration )
{
ClientInfo client = (ClientInfo) handlerRegistration;
client.callback = null;
client.cookie = null;
clients.remove( client );
if( clients.isEmpty() )
clients = null;
// remove adapters
for( int i=0; i<adapters.length; i++ )
{
if( adapters[i] == null )
continue;
adapters[i].removePropertyChangedHandler( adapterHandlerRegistrations[i] );
}
}
// create adapaters from the root context object to the end of the path, if
// possible...
private void tryCreateAdapters()
{
Object object = context;
for( int p = 0; p < path.length; p++ )
{
if( object == null )
return;
// if no adapter has yet been created for this pathItem
if( adapters[p] == null )
{
String pathItem = path[p];
// try to find an adapter, otherwise create one or return null
// to create an adapter, we need a context and a path item
// context is the 'object' value (ie the value of the previous
// pathItem or the root context)
// path item is path[p]
if(pathItem.charAt(0)=='$')
{
if( PlatformSpecificProvider.get().isBindingToken( pathItem ) )
adapters[p] = PlatformSpecificProvider.get().createPropertyAdapter(object);
else if( CompositePropertyAdapter.DTOMAP_TOKEN.equals( pathItem ) )
adapters[p] = new DTOMapperPropertyAdapter( object );
}
else
{
adapters[p] = new ObjectPropertyAdapter( object, pathItem );
}
// we should subscribe to the value changes so that we can
// subscribe to
// new values when anything on the path changes
adapterHandlerRegistrations[p] = adapters[p].registerPropertyChanged( onPropertyChanged, p );
}
if( p < path.length - 1 )
object = adapters[p].getValue();
}
}
}