package fr.lteconsulting.hexa.databinding.properties;
import java.util.ArrayList;
import java.util.HashMap;
import fr.lteconsulting.hexa.databinding.INotifyPropertyChanged;
import fr.lteconsulting.hexa.databinding.PlatformSpecificProvider;
/**
* This class is part of the Hexa DataBinding and provides :
* <ol>
* <li>registering to object's property change
* <li>notifying when a property changes in an object
* </ol>
*
* @author Arnaud Tournier
*
*/
public class PropertyChanges
{
private final PropertyChangesStatistics stats = new PropertyChangesStatistics();
/**
* Registers an handler for a specific property change on an object. The object
* itself does not need to implement anything. But at least it should call the
* {@link notify(Object, String)} method when its internal property changes.
*
* @param source The object from which one wants notifications
* @param propertyName The property subscribed. You can use "*" to subscribe to all properties in one time
* @param handler
* @return
*/
Object register( Object source, String propertyName, PropertyChangedHandler handler )
{
assert source != null;
if( source instanceof INotifyPropertyChanged )
{
DirectHandlerInfo info = new DirectHandlerInfo();
info.source = (INotifyPropertyChanged) source;
info.registrationObject = info.source.registerPropertyChangedEvent( propertyName, handler );
return info;
}
HashMap<String, ArrayList<PropertyChangedHandler>> handlersMap = PlatformSpecificProvider.get().getObjectMetadata( source );
if( handlersMap == null )
{
handlersMap = new HashMap<>();
PlatformSpecificProvider.get().setObjectMetadata( source, handlersMap );
}
ArrayList<PropertyChangedHandler> handlerList = handlersMap.get( propertyName );
if( handlerList == null )
{
handlerList = new ArrayList<PropertyChangedHandler>();
handlersMap.put( propertyName, handlerList );
}
handlerList.add( handler );
HandlerInfo info = new HandlerInfo();
info.source = source;
info.propertyName = propertyName;
info.handler = handler;
stats.statsAddedRegistration( info );
return info;
}
/**
* Unregisters a handler, freeing associated resources
*
* @param handlerRegistration The object received after a call to {@link PropertyChanges}
*/
void removeHandler( Object handlerRegistration )
{
// Through object's interface implementation
if( handlerRegistration instanceof DirectHandlerInfo )
{
DirectHandlerInfo info = (DirectHandlerInfo) handlerRegistration;
info.source.removePropertyChangedHandler( info.registrationObject );
return;
}
if( ! ( handlerRegistration instanceof HandlerInfo ) )
return;
HandlerInfo info = (HandlerInfo) handlerRegistration;
HashMap<String, ArrayList<PropertyChangedHandler>> handlersMap = PlatformSpecificProvider.get().getObjectMetadata( info.source );
if( handlersMap == null )
return;
ArrayList<PropertyChangedHandler> handlerList = handlersMap.get( info.propertyName );
if( handlerList == null )
return;
handlerList.remove( info.handler );
if( handlerList.isEmpty() )
handlersMap.remove( info.propertyName );
if( handlersMap.isEmpty() )
PlatformSpecificProvider.get().setObjectMetadata( info.source, null );
stats.statsRemovedRegistration( info );
info.handler = null;
info.propertyName = null;
info.source = null;
}
/**
* Notifies the Hexa event system of an object changing one of
* its properties.
*
* @param sender The object whom property changed
* @param propertyName The changed property name
*/
void notify( Object sender, String propertyName )
{
stats.addNotification();
HashMap<String, ArrayList<PropertyChangedHandler>> handlersMap = PlatformSpecificProvider.get().getObjectMetadata( sender );
if( handlersMap == null )
return;
PropertyChangedEvent event = null;
ArrayList<PropertyChangedHandler> handlerList = handlersMap.get( propertyName );
if( handlerList != null )
{
if( event == null )
event = new PropertyChangedEvent( sender, propertyName );
for( PropertyChangedHandler handler : handlerList )
{
handler.onPropertyChanged( event );
stats.addDispatch();
}
}
handlerList = handlersMap.get( "*" );
if( handlerList != null )
{
if( event == null )
event = new PropertyChangedEvent( sender, propertyName );
for( PropertyChangedHandler handler : handlerList )
{
handler.onPropertyChanged( event );
stats.addDispatch();
}
}
}
/**
* Show an alert containing useful information for debugging. It also
* shows how many registrations happened since last call ; that's useful
* to detect registration leaks.
*/
String getStatistics()
{
return stats.getStatistics();
}
private static class DirectHandlerInfo
{
INotifyPropertyChanged source;
Object registrationObject;
}
public static class HandlerInfo
{
public Object source;
public String propertyName;
public PropertyChangedHandler handler;
}
}