package jadex.rules.state.javaimpl;
import jadex.commons.collection.IdentityHashSet;
import jadex.rules.state.IOAVState;
import jadex.rules.state.IOAVStateListener;
import jadex.rules.state.OAVAttributeType;
import jadex.rules.state.OAVObjectType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* This class handles the collection and distribution OAV
* events to registered listeners.
*/
public class OAVEventHandler
{
//-------- attributes --------
/** The state. */
protected IOAVState state;
/** The bunch state listeners. */
protected List listeners;
/** The direct state listeners. */
protected List directlisteners;
/** The collected change events. */
protected Set oavevents;
/** The collected bean events (may be added from external thread). */
protected Set beanevents;
/** The objects that have been removed in current change set. */
protected Set removed_objects;
/** The objects that have been added in current change set. */
protected Set added_objects;
/** Flag that is only true, while listeners are being notified. */
protected boolean notifying;
//-------- constructors --------
/**
* Create a new OAV event handler.
*/
public OAVEventHandler(IOAVState state)
{
this.state = state;
this.listeners = new ArrayList();
this.directlisteners = new ArrayList();
this.oavevents = new LinkedHashSet();
// this.oavevents = new CheckedCollection(new LinkedHashSet());
this.beanevents = Collections.synchronizedSet(new LinkedHashSet());
// this.beanevents = Collections.synchronizedSet(new CheckedCollection(new LinkedHashSet()));
}
//-------- state observers --------
/**
* Add a new state listener.
* @param listener The state listener.
*/
public void addStateListener(IOAVStateListener listener, boolean bunch)
{
if(bunch)
this.listeners.add(listener);
else
this.directlisteners.add(listener);
}
/**
* Remove a state listener.
* @param listener The state listener.
*/
public void removeStateListener(IOAVStateListener listener)
{
if(!this.listeners.remove(listener))
if(!this.directlisteners.remove(listener))
throw new RuntimeException("Listener not found: "+listener);
}
/**
* Throw collected events and notify the listeners.
*/
public void notifyEventListeners()
{
this.notifying = true;
if(!beanevents.isEmpty())
{
// Set bean events is locked during event processing
// This avoids copy to array (but with shorter lock)
synchronized(beanevents)
{
for(Iterator it=beanevents.iterator(); it.hasNext(); )
{
notifyOneEvent(it.next());
// it.remove();
}
beanevents.clear();
}
}
for(Iterator it=oavevents.iterator(); it.hasNext(); )
{
notifyOneEvent(it.next());
// it.remove();
}
oavevents.clear();
removed_objects = null;
added_objects = null;
this.notifying = false;
}
/**
* Notify one event to all listeners.
* @param evt The event.
*/
protected void notifyOneEvent(Object evt)
{
// System.out.println("notify: "+evt);
if(evt instanceof OAVObjectAddedEvent)
{
OAVObjectAddedEvent event = (OAVObjectAddedEvent)evt;
if(removed_objects==null || !removed_objects.contains(event.id))
{
for(int i=0; i<listeners.size(); i++)
{
((IOAVStateListener)listeners.get(i)).objectAdded(event.id, event.type, event.root);
}
}
// else
// {
// System.out.println("Ignored added object: "+event.id);
// }
}
else if(evt instanceof OAVObjectRemovedEvent)
{
OAVObjectRemovedEvent event = (OAVObjectRemovedEvent)evt;
if(added_objects==null || !added_objects.contains(event.id))
{
for(int i=0; i<listeners.size(); i++)
{
((IOAVStateListener)listeners.get(i)).objectRemoved(event.id, event.type);
}
}
}
else //if(event instanceof OAVObjectModifiedEvent)
{
OAVObjectModifiedEvent event = (OAVObjectModifiedEvent)evt;
// System.out.println("notify: "+event);
if((added_objects==null || !added_objects.contains(event.id))
&& (removed_objects==null || !removed_objects.contains(event.id)))
{
for(int i=0; i<listeners.size(); i++)
{
((IOAVStateListener)listeners.get(i)).objectModified(event.id, event.type,
event.attribute, event.oldvalue, event.newvalue);
}
}
// else
// {
// System.out.println("Ignored modified object: "+event.id);
// }
}
}
//-------- event methods --------
/**
* Notification when an attribute value of an object has been set.
* @param id The object id.
* @param type The object type.
* @param attr The attribute type.
* @param oldvalue The oldvalue.
* @param newvalue The newvalue.
*/
public void objectModified(Object id, OAVObjectType type, OAVAttributeType attr, Object oldvalue, Object newvalue)
{
if(!listeners.isEmpty())
{
if((added_objects==null || !added_objects.contains(id))
&& (removed_objects==null || !removed_objects.contains(id)))
{
OAVObjectModifiedEvent evt = new OAVObjectModifiedEvent(state, id, type, attr, oldvalue, newvalue);
oavevents.remove(evt); // All events are necessary for external listeners
oavevents.add(evt);
}
}
for(int i=0; i<directlisteners.size(); i++)
((IOAVStateListener)directlisteners.get(i)).objectModified(id, type, attr, oldvalue, newvalue);
}
/**
* Notification when an attribute value of a bean has been set.
* @param bean The bean.
* @param type The object type.
* @param attr The attribute type.
* @param oldvalue The oldvalue.
* @param newvalue The newvalue.
*/
public void beanModified(Object bean, OAVObjectType type, OAVAttributeType attr, Object oldvalue, Object newvalue)
{
synchronized(beanevents)
{
OAVObjectModifiedEvent evt = new OAVObjectModifiedEvent(state, bean, type, attr, oldvalue, newvalue);
if(!listeners.isEmpty())
{
beanevents.remove(evt);
beanevents.add(evt);
}
for(int i=0; i<directlisteners.size(); i++)
((IOAVStateListener)directlisteners.get(i)).objectModified(bean, type, attr, oldvalue, newvalue);
}
}
/**
* Notification when an object has been added to the state.
* @param id The object id.
* @param type The object type.
*/
public void objectAdded(Object id, OAVObjectType type, boolean root)
{
// System.out.println("added: "+id+" "+type);
// Thread.dumpStack();
// if(type instanceof OAVJavaType && ((OAVJavaType)type).getClazz().getName().indexOf("Wastebin")!=-1)
// {
// System.out.println("added: "+id);
// }
if(!listeners.isEmpty())
{
if(removed_objects==null || !removed_objects.contains(id))
{
oavevents.add(new OAVObjectAddedEvent(id, type, root));
}
else
{
removed_objects.remove(id);
}
if(added_objects==null)
added_objects = createIdSet();
added_objects.add(id);
}
for(int i=0; i<directlisteners.size(); i++)
((IOAVStateListener)directlisteners.get(i)).objectAdded(id, type, root);
}
/**
* Notification when an object has been removed from state.
* @param id The object id.
* @param type The object type.
*/
public void objectRemoved(Object id, OAVObjectType type/*, Map content*/)
{
// if(type instanceof OAVJavaType && ((OAVJavaType)type).getClazz().getName().indexOf("Wastebin")!=-1)
// {
// System.out.println("removed: "+id);
// }
/*if(id.toString().indexOf("id=Chargingstation")!=-1)
{
System.out.println("Removed: "+id);
Thread.dumpStack();
}*/
if(!listeners.isEmpty())
{
if(added_objects==null || !added_objects.contains(id))
{
oavevents.add(new OAVObjectRemovedEvent(id, type));
}
else
{
added_objects.remove(id);
}
if(removed_objects==null)
removed_objects = createIdSet();
removed_objects.add(id);
}
for(int i=0; i<directlisteners.size(); i++)
((IOAVStateListener)directlisteners.get(i)).objectRemoved(id, type);
}
/**
* Create a set for holding object ids.
*/
protected Set createIdSet()
{
return state.isJavaIdentity() ? (Set)new IdentityHashSet() : new HashSet();
}
}