Initial commit from SVN.

This commit is contained in:
wcrisman
2014-05-30 10:31:51 -07:00
commit b45e56b890
1968 changed files with 370949 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
package com.foundation.event;
import com.common.debug.Debug;
import com.common.thread.IRunnable;
import com.common.thread.ISingleThreadedContext;
import com.common.thread.ThreadService;
import com.common.util.LiteList;
/**
* This request handler is designed to not have a dedicated thread for processing incoming runnables.
* Instead of a dedicated thread, this handler uses the calling thread for synchronous calls, and a reusable thread for asynchronous calls.
* Since there isn't a dedicated thread, the handler does not ever need to be stopped or started.
*/
public class CustomRequestHandler implements IRequestHandler, ISingleThreadedContext {
private volatile Thread requestThread = null;
private LiteList requestQueue = new LiteList(10);
/** Whether to allow potential deadlock situations. */
private boolean allowPotentialDeadlockSituations = false;
/**
* Used to kick off the next queued runnable (or request) in a reusable thread.
* The reusable thread will keep processing queued runnables until none are left or a synchronous runnable is encountered.
*/
private class AsyncRunnable implements Runnable {
public void run() {
try {
IRunnable runnable = null;
synchronized(CustomRequestHandler.this) {
//Make this thread the requestThread.//
requestThread = Thread.currentThread();
//Flag this thread as running in this reflection context.//
ThreadService.setIsInSingleThreadedContext(CustomRequestHandler.this);
//Get the runnable.//
try {
runnable = (IRunnable) requestQueue.remove(0);
}//try//
catch(Throwable e) {
Debug.log(e);
}//catch//
}//synchronized//
while(runnable != null) {
//Run the runnable.//
try {
runnable.run();
}//try//
catch(Throwable e) {
Debug.log(e);
}//catch//
synchronized(CustomRequestHandler.this) {
if(requestQueue.getSize() > 0) {
IRunnable next = (IRunnable) requestQueue.get(0);
if(next instanceof SynchronousRunnable) {
//Kick off the thread waiting to run the next runnable.//
((SynchronousRunnable) next).notifyOnQueue();
//Exit the loop and return the thread.//
runnable = null;
//Clear the single threaded context flag.//
ThreadService.setIsInSingleThreadedContext(null);
//Clear the current thread as the request thread.//
requestThread = null;
}//if//
else {
runnable = next;
requestQueue.remove(0);
}//else//
}//if//
else {
//Exit the loop and return the thread.//
runnable = null;
//Clear the single threaded context flag.//
ThreadService.setIsInSingleThreadedContext(null);
//Clear the current thread as the request thread.//
requestThread = null;
}//else//
}//synchronized//
}//while//
}//try//
catch(Throwable e) {
Debug.log(e);
//Clear the single threaded context flag.//
ThreadService.setIsInSingleThreadedContext(null);
synchronized(CustomRequestHandler.this) {
requestThread = null;
}//synchronized//
}//catch//
}//run()//
}//AsyncRunnable//
/**
* A simple runnable that wrappers another runnable and notifies waiting threads when the execution is complete.
*/
private static class SynchronousRunnable implements IRunnable {
private IRunnable runnable = null;
private boolean isReady = false;
/**
* SynchronousRunnable constructor.
* @param runnable The runnable that will be executed and whose result will be passed to waiting threads.
*/
public SynchronousRunnable(IRunnable runnable) {
this.runnable = runnable;
}//SynchronousRunnable()//
/* (non-Javadoc)
* @see com.common.thread.IRunnable#run()
*/
public Object run() {
return runnable.run();
}//run()//
public void notifyOnQueue() {
synchronized(this) {
isReady = true;
notify();
}//synchronized//
}//notifyOnQueue()//
public void waitOnQueue() {
synchronized(this) {
while(!isReady) {
try {
wait();
}//try//
catch(Throwable e) {
Debug.handle(e);
}//catch//
}//while//
}//synchronized//
}//waitOnQueue()//
}//SynchronousRunnable//
/**
* CustomRequestHandler constructor.
*/
public CustomRequestHandler() {
}//CustomRequestHandler()//
/* (non-Javadoc)
* @see com.foundation.event.IRequestHandler#isRunning()
*/
public boolean isRunning() {
//Will always be true because this request handler does not have a dedicated thread there is never a need to stop it.//
return true;
}//isRunning()//
/* (non-Javadoc)
* @see com.foundation.event.IRequestHandler#execute(com.common.thread.IRunnable, boolean)
*/
public Object execute(IRunnable runnable, boolean synchronous) {
Object result = null;
boolean runIt = false;
boolean runImmediately = false;
Thread thisThread = Thread.currentThread();
ISingleThreadedContext oldContext = null;
synchronized(this) {
//If another thread isn't busy using the context then start immediately.//
if(requestThread == null && requestQueue.getSize() == 0) {
if(synchronous) {
if((oldContext = ThreadService.getSingleThreadedContext()) != null) {
if(!allowPotentialDeadlockSituations) {
throw new RuntimeException("Warning: Potential deadlock scenario detected. A thread should not be the processing thread for more than one IRequestHandler, or any other single threaded context.");
}//if//
}//if//
//Make this thread the requestThread.//
requestThread = thisThread;
//Flag this thread as running in this reflection context.//
ThreadService.setIsInSingleThreadedContext(this);
//Flag to run below.//
runIt = true;
}//if//
else {
//Queue the runnable.//
requestQueue.add(runnable);
runnable = null;
//Run the runnable in a reusable thread.//
ThreadService.run(new AsyncRunnable());
}//else//
}//if//
else if(requestThread == thisThread && synchronous) {
runImmediately = true;
}//else if//
else {
//Queue the runnable.//
if(synchronous) {
requestQueue.add(runnable = new SynchronousRunnable(runnable));
//Flag to run below.//
runIt = true;
}//if//
else {
requestQueue.add(runnable);
}//else//
}//else//
}//synchronized//
//Make sure to run synchronous tasks that are initiated by the request thread without any of the hoopla.//
if(runImmediately) {
try {
result = runnable.run();
}//try//
catch(Throwable e) {
Debug.log(e);
}//catch//
}//if//
//If we need to run the runnable, or wait and then run.//
if(runIt) {
//If we must wait to process the runnable later then do that now.//
if(runnable instanceof SynchronousRunnable) {
//Wait our turn to run.//
((SynchronousRunnable) runnable).waitOnQueue();
if((oldContext = ThreadService.getSingleThreadedContext()) != null) {
if(!allowPotentialDeadlockSituations) {
throw new RuntimeException("Warning: Potential deadlock scenario detected. A thread should not be the processing thread for more than one IRequestHandler, or any other single threaded context.");
}//if//
}//if//
//Set the single threaded context flag.//
ThreadService.setIsInSingleThreadedContext(this);
synchronized(this) {
//Make this thread the requestThread.//
requestThread = Thread.currentThread();
}//synchronized//
}//if//
//Run the runnable and collect the result.//
try {
result = runnable.run();
}//try//
catch(Throwable e) {
Debug.log(e);
}//catch//
//Clear the single threaded context flag (or set it to the old context).//
ThreadService.setIsInSingleThreadedContext(oldContext);
//Start the next queued runnable if there is one.//
synchronized(this) {
//Clear the current thread as the request thread.//
requestThread = null;
if(requestQueue.getSize() > 0) {
IRunnable next = (IRunnable) requestQueue.get(0);
if(next instanceof SynchronousRunnable) {
//Kick off the thread waiting to run the next runnable.//
((SynchronousRunnable) next).notifyOnQueue();
//Remove the runnable from the queue.//
requestQueue.remove(0);
}//if//
else {
//Run the next runnable in a reusable thread.//
ThreadService.run(new AsyncRunnable());
}//else//
}//if//
}//synchronized//
}//if//
return result;
}//execute()//
/* (non-Javadoc)
* @see com.foundation.event.IRequestHandler#executeAsync(com.common.thread.IRunnable)
*/
public void executeAsync(IRunnable runnable) {
execute(runnable, false);
}//executeAsync()//
/* (non-Javadoc)
* @see com.foundation.event.IRequestHandler#execute(com.common.thread.IRunnable)
*/
public Object execute(IRunnable runnable) {
return execute(runnable, true);
}//execute()//
/* (non-Javadoc)
* @see com.foundation.event.IRequestHandler#isRequestThread()
*/
public synchronized boolean isRequestThread() {
return Thread.currentThread() == requestThread;
}//isRequestThread()//
}//CustomRequestHandler//

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
import com.common.orb.*;
import com.common.util.*;
import com.common.debug.*;
public class EventNotifier {
/** The one and only notifier instance. */
private static final EventNotifier singleton = new EventNotifier();
/** Save a reference to the thread in case we wish to change its priority or name. */
private static final Thread notifierThread;
/** A queue containing event data. */
private Queue eventQueue = new Queue(50);
/** A flag used to stop the notifier thread when shutting down. */
private boolean stop = false;
static {
notifierThread = new Thread(new Runnable() {
public void run() {
singleton.run();
}//run()//
});
notifierThread.setName("Event Notifier");
notifierThread.start();
}//static//
/**
* Encapsulates a single event's data.
*/
private static class EventData {
private IEventEmitter emitter = null;
private int number = -1;
private Object[] parameters = null;
private ICollection listeners = null;
private boolean makeAynchronousRemoteCalls = false;
private int flags = 0;
private EventData() {
}//EventData()//
private EventData(IEventEmitter emitter, int number, Object[] parameters, ICollection listeners, boolean makeAynchronousRemoteCalls, int flags) {
this.emitter = emitter;
this.number = number;
this.parameters = parameters;
this.listeners = listeners;
this.makeAynchronousRemoteCalls = makeAynchronousRemoteCalls;
this.flags = flags;
}//EventData()//
}//EventData()/
/**
* EventNotifier constructor.
*/
private EventNotifier() {
super();
}//EventNotifier()//
/**
* Gets the one and only event notifier instance.
* @return The singleton reference to the event notifier.
*/
public static EventNotifier getSingleton() {
return singleton;
}//getSingleton()//
/**
* Manages an event such that it will be processed in order that is was fired relative to all other events on this process.
* <p>This method should only be called by the event support class.</p>
*/
void manageEvent(IEventEmitter emitter, int number, Object[] parameters, ICollection listeners, boolean makeAynchronousRemoteCalls, int flags) {
synchronized(eventQueue) {
eventQueue.enqueue(new EventData(emitter, number, parameters, listeners, makeAynchronousRemoteCalls, flags));
eventQueue.notify();
}//synchronized//
}//manageEvent()//
/**
* Runs the event notifier which will handle executing each event in order of its occurance.
*/
private void run() {
try {
while((eventQueue.getSize() > 0) || (!stop)) {
EventData eventData = null;
synchronized(eventQueue) {
while((!stop) && (eventQueue.getSize() == 0)) {
eventQueue.wait(10000);
}//while//
if(eventQueue.getSize() > 0) {
eventData = (EventData) eventQueue.dequeue();
}//if//
}//synchronized//
if(eventData != null) {
try {
IIterator iterator = eventData.listeners.iterator();
while(iterator.hasNext()) {
IHandler handler = (IHandler) iterator.next();
if((eventData.makeAynchronousRemoteCalls) && (Orb.isProxy(handler))) {
Orb.setOneWayCall(handler);
}//if//
if(handler instanceof IEventHandler) {
((IEventHandler) handler).evaluate(Orb.isLocal(handler) ? eventData.emitter : (IEventEmitter) Orb.getProxy(eventData.emitter, IEventEmitter.class), eventData.number, eventData.parameters, eventData.flags);
}//if//
else {
handler.evaluate(eventData.number, eventData.parameters, eventData.flags);
}//else//
}//while//
}//try//
catch(Throwable e) {
Debug.log(e, "Caught while executing an event handler.");
}//catch//
}//if//
}//while//
}//try//
catch(InterruptedException e) {
//Ignore. Let the thread die.//
}//catch//
}//run()//
/**
* Stops the event notifier thread if it is running.
*/
public void stop() {
stop = true;
}//stop()//
}//EventNotifier//

View File

@@ -0,0 +1,897 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
import com.common.orb.*;
import com.common.util.*;
import com.common.comparison.Comparator;
import com.common.debug.*;
import com.common.util.optimized.*;
import com.foundation.metadata.Event;
import com.foundation.metadata.MetadataService;
/*
* Supports any class implementing the IEventEmitter interface.
* <p>This class is serializable, but it is intended only for moving an object from one VM to another via an Orb.</p>
*/
public class EventSupport implements java.io.Externalizable {
/** Whether the event is due to a change that was trusted. A trusted change is one that altered data to be what it should have been all along. A lazy load is an example of a trusted change. */
public static final int FLAG_TRUSTED_CHANGE = 0x01;
/** Whether the event is due to a change in the underlying original value. So the current value hasn't changed, but an update to the original value has occured. This is used with attributes that have the concept of an original value and a current value. */
public static final int FLAG_ORIGINAL_VALUE_CHANGED = 0x02;
/** Whether the event is due to a change in the underlying original value. So the current value hasn't changed, but an update to the original value has occured. This is used with attributes that have the concept of an original value and a current value. */
public static final int FLAG_ORIGINAL_VALUE_CLEARED = 0x04;
/** Whether the event is due to the reflected object updating the object firing the event. */
public static final int FLAG_REFLECTION_UPDATE = 0x08;
/** The event emitter being supported. */
private IEventEmitter supportedObject = null;
/** A mapping of ListenerSets indexed by the event number. */
private IntObjectHashMap threadedListenerMap = new IntObjectHashMap(20);
/** A mapping of event handler by number maps indexed by the emitter of the event. */
private IHashMap emitterMap = new LiteHashMap(10);
/**
* Encapsulates inlined and threaded listener sets for a specific event.
*/
private static class ListenerSets {
private IHashSet inlinedListeners = null;
private IHashSet threadedListeners = null;
public ListenerSets() {
}//ListenerSets()//
public void addListener(IHandler listener, boolean inlined) {
if(((inlinedListeners == null) || (!inlinedListeners.containsValue(listener))) && ((threadedListeners == null) || (!threadedListeners.containsValue(listener)))) {
if(inlined) {
if(inlinedListeners == null) {
inlinedListeners = new LiteHashSet(8, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getIdentityComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
}//if//
inlinedListeners.add(listener);
}//if//
else {
if(threadedListeners == null) {
threadedListeners = new LiteHashSet(8, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getIdentityComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
}//if//
threadedListeners.add(listener);
}//else//
}//if//
else {
Debug.log("Error: Duplicate event listener added to the listener set!", new RuntimeException(), true);
}//else//
}//addListener()//
public void removeListener(IHandler listener) {
if(((inlinedListeners == null) || (!inlinedListeners.remove(listener))) && (threadedListeners != null)) {
threadedListeners.remove(listener);
}//if//
//Cleanup the collections.//
if((inlinedListeners != null) && (inlinedListeners.getSize() == 0)) {
inlinedListeners = null;
}//if//
if((threadedListeners != null) && (threadedListeners.getSize() == 0)) {
threadedListeners = null;
}//if//
}//removeListener()//
public int getListenerCount() {
return (inlinedListeners != null ? inlinedListeners.getSize() : 0) + (threadedListeners != null ? threadedListeners.getSize() : 0);
}//getListenerCount()//
public IHashSet getListeners(boolean inlined) {
return inlined ? inlinedListeners : threadedListeners;
}//getListeners()//
}//ListenerSets//
/**
* Gets the event name for the given event number.
* @param type The class that defines (directly or indirectly) the event.
* @param eventNumber The unqiue (within the context of the type hierarchy) number of the event.
* @return The event name, or null if the event could not be found.
*/
public static String getEventName(Class type, int eventNumber) {
return MetadataService.getSingleton().getEventName(type, eventNumber);
}//getEventName()//
/**
* Gets the event number for the given event name.
* @param type The class that defines (directly or indirectly) the event.
* @param eventName The unqiue (within the context of the type hierarchy) name of the event.
* @return The event number, or -1 if the event could not be found.
*/
public static int getEventNumber(Class type, String eventName) {
return MetadataService.getSingleton().getEventNumber(type, eventName);
}//getEventNumber()//
/**
* Determines whether the event that passed the given event flags is a standard change event.
* @param eventFlags The event's flags.
* @return Whether the event is considered to be standard. Most event listeners are only interested in standard events that indicate a value has really been changed.
*/
public static boolean isStandardEvent(int eventFlags) {
return eventFlags == 0;
}//isStandardEvent()//
/**
* Determines whether the event that passed the given event flags is a change under a trusted context.
* @param eventFlags The event's flags.
* @return Whether the change represented by the event is a trusted change, meaning that most listeners should ignore it, but some may need to take action.
* @see #FLAG_TRUSTED_CHANGE
*/
public static boolean isTrustedChangeEvent(int eventFlags) {
return (eventFlags & FLAG_TRUSTED_CHANGE) > 0;
}//isTrustedChangeEvent()//
/**
* Determines whether the event that passed the given event flags is a change due to the reflected object updating its reflections.
* @param eventFlags The event's flags.
* @return Whether the change represented by the event is due to an update from the reflected object.
* @see #FLAG_REFLECTION_UPDATE
*/
public static boolean isReflectionUpdateChangeEvent(int eventFlags) {
return (eventFlags & FLAG_REFLECTION_UPDATE) > 0;
}//isReflectionUpdateChangeEvent()//
/**
* Determines whether the event that passed the given event flags is indicating that the original value has been altered.
* An event with this flag does not indicate a change in the current value. This is used only when the value has been altered locally and a remote update alters the value that the current value was originally based on.
* @param eventFlags The event's flags.
* @return Whether the change represented by the event is just changing the original value and not the current value.
* @see #FLAG_ORIGINAL_VALUE_CHANGED
*/
public static boolean isOriginalValueChanged(int eventFlags) {
return (eventFlags & FLAG_ORIGINAL_VALUE_CHANGED) > 0;
}//isOriginalValueChanged()//
/**
* Determines whether the event that passed the given event flags is indicating that the original value has been cleared.
* An event with this flag does not indicate a change in the current value. This is used only when the value has been altered locally and a remote update alters the value that the current value was originally based on.
* @param eventFlags The event's flags.
* @return Whether the change represented by the event is just changing the original value and not the current value.
* @see #FLAG_ORIGINAL_VALUE_CHANGED
*/
public static boolean isOriginalValueCleared(int eventFlags) {
return (eventFlags & FLAG_ORIGINAL_VALUE_CLEARED) > 0;
}//isOriginalValueCleared()//
/**
* EventSupport constructor.
*/
protected EventSupport() {
super();
}//EventSupport()//
/**
* EventSupport constructor.
* @param supportedObject The object that is being supported. This is required if events will be fired. This is optional if the support object is only being used to listen for events fired by other objects.
*/
public EventSupport(IEventEmitter supportedObject) {
super();
this.supportedObject = supportedObject;
}//EventSupport()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
*/
public void fireEvent(Event event) {
fireEvent(event.getNumber(), new Object[0], false, 0);
}//fireEvent()//
/**
* Fires an event with an array of parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters The array of all parameters.
*/
public void fireEvent(Event event, Object[] parameters) {
fireEvent(event.getNumber(), parameters, false, 0);
}//fireEvent()//
/**
* Fires an event with an array of parameters.
* @param eventName The name of the event to be fired.
* @param parameters The array of all parameters.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response). This should almost always be true.
* @param isChangeTrusted Whether the change was a trusted change. A trusted change is one that altered data to be what it should have been all along. A lazy load is an example of a trusted change.
*/
public void fireEvent(Event event, Object[] parameters, boolean makeAynchronousRemoteCalls, int flags) {
fireEvent(event.getNumber(), parameters, false, flags);
}//fireEvent()//
/**
* Fires an event with one parameter.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
*/
public void fireEvent(Event event, Object parameter) {
fireEvent(event.getNumber(), new Object[] {parameter}, false, 0);
}//fireEvent()//
/**
* Fires an event with two parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
*/
public void fireEvent(Event event, Object parameter1, Object parameter2) {
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2}, false, 0);
}//fireEvent()//
/**
* Fires an event with three parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param parameters3 The third parameter.
*/
public void fireEvent(Event event, Object parameter1, Object parameter2, Object parameter3) {
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2, parameter3}, false, 0);
}//fireEvent()//
/**
* Fires an event with three parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param parameters3 The third parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(Event event, Object parameter1, Object parameter2, Object parameter3, boolean makeAynchronousRemoteCalls) {
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2, parameter3}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with two parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(Event event, Object parameter1, Object parameter2, boolean makeAynchronousRemoteCalls) {
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with one parameter.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(Event event, Object parameter, boolean makeAynchronousRemoteCalls) {
fireEvent(event.getNumber(), new Object[] {parameter}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(Event event, boolean makeAynchronousRemoteCalls) {
fireEvent(event.getNumber(), new Object[0], makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
* @param flags The event dependant flags.
*/
public void fireEvent(Event event, boolean makeAynchronousRemoteCalls, int flags) {
fireEvent(event.getNumber(), new Object[0], makeAynchronousRemoteCalls, flags);
}//fireEvent()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
*/
public void fireEvent(int eventNumber) {
fireEvent(eventNumber, new Object[0], false, 0);
}//fireEvent()//
/**
* Fires an event with an array of parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters The array of all parameters.
*/
public void fireEvent(int eventNumber, Object[] parameters) {
fireEvent(eventNumber, parameters, false, 0);
}//fireEvent()//
/**
* Fires an event with an array of parameters.
* @param eventName The name of the event to be fired.
* @param parameters The array of all parameters.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response). This should almost always be true.
* @param flags
*/
public void fireEvent(int eventNumber, Object[] parameters, boolean makeAynchronousRemoteCalls, int flags) {
if(hasListeners(eventNumber)) {
try {
ListenerSets listeners = getListeners(eventNumber);
IHashSet inlinedListeners = listeners.getListeners(true);
IHashSet threadedListeners = listeners.getListeners(false);
//Call the inlined listeners with this thread. Note that this is a performance problem if the handler is not fast.//
//This is necessary for the view code since it dramatically improves performance since events get processed directly on the event thread (views and associated models are single threaded).//
if((inlinedListeners != null) && (inlinedListeners.getSize() > 0)) {
Object[] inlinedListenerArray = inlinedListeners.toArray();
for(int index = 0; index < inlinedListenerArray.length; index++) {
IHandler handler = (IHandler) inlinedListenerArray[index];
if((makeAynchronousRemoteCalls) && (Orb.isProxy(handler))) {
Orb.setOneWayCall(handler);
}//if//
if(handler instanceof IEventHandler) {
((IEventHandler) handler).evaluate(Orb.isLocal(handler) ? getSupportedObject() : (IEventEmitter) Orb.getProxy(getSupportedObject(), IEventEmitter.class), eventNumber, parameters, flags);
}//if//
else {
handler.evaluate(eventNumber, parameters, flags);
}//else//
}//while//
}//if//
//Call the threaded listeners (most non-gui listeners) using the event notifier which will preserve the ordering of the events and optimize thread useage.//
if((threadedListeners != null) && (threadedListeners.getSize() > 0)) {
ICollection threadedListenerList = new LiteList((ICollection) threadedListeners);
EventNotifier.getSingleton().manageEvent(getSupportedObject(), eventNumber, parameters, threadedListenerList, makeAynchronousRemoteCalls, flags);
}//if//
}//try//
catch(Throwable e) {
Debug.log(e);
Debug.halt();
}//catch//
}//if//
}//fireEvent()//
/**
* Fires an event with one parameter.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
*/
public void fireEvent(int eventNumber, Object parameter) {
fireEvent(eventNumber, new Object[] {parameter}, false, 0);
}//fireEvent()//
/**
* Fires an event with two parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
*/
public void fireEvent(int eventNumber, Object parameter1, Object parameter2) {
fireEvent(eventNumber, new Object[] {parameter1, parameter2}, false, 0);
}//fireEvent()//
/**
* Fires an event with three parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param parameters3 The third parameter.
*/
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, Object parameter3) {
fireEvent(eventNumber, new Object[] {parameter1, parameter2, parameter3}, false, 0);
}//fireEvent()//
/**
* Fires an event with three parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param parameters3 The third parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, Object parameter3, boolean makeAynchronousRemoteCalls) {
fireEvent(eventNumber, new Object[] {parameter1, parameter2, parameter3}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with two parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param parameters2 The second parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, boolean makeAynchronousRemoteCalls) {
fireEvent(eventNumber, new Object[] {parameter1, parameter2}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with one parameter.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param parameters1 The first parameter.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(int eventNumber, Object parameter, boolean makeAynchronousRemoteCalls) {
fireEvent(eventNumber, new Object[] {parameter}, makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
*/
public void fireEvent(int eventNumber, boolean makeAynchronousRemoteCalls) {
fireEvent(eventNumber, new Object[0], makeAynchronousRemoteCalls, 0);
}//fireEvent()//
/**
* Fires an event with no parameters.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
* @param flags The event dependant flags.
*/
public void fireEvent(int eventNumber, boolean makeAynchronousRemoteCalls, int flags) {
fireEvent(eventNumber, new Object[0], makeAynchronousRemoteCalls, flags);
}//fireEvent()//
/**
* Gets the listeners for a perticular event.
* @param eventNumber The unqiue number of the event the listeners are listening to.
* @return The collection of inlined and threaded listeners.
*/
private ListenerSets getListeners(int eventNumber) {
return (ListenerSets) threadedListenerMap.get(eventNumber);
}//getListeners()//
/**
* Gets or creates the event hashmap which indexes a single IHandler or a set of IHandlers by the event the handlers process.
* @param eventEmitter The emitter whose event map is to be retrieved.
* @return The mapping of event handlers by event number.
*/
private IntObjectHashMap getOrCreateEventMap(IEventEmitter eventEmitter) {
IntObjectHashMap result = (IntObjectHashMap) emitterMap.get(eventEmitter);
if(result == null) {
result = new IntObjectHashMap(10);
emitterMap.put(eventEmitter, result);
}//if//
return result;
}//getOrCreateEventMap()//
/**
* Retrieves a collection of listeners listening to a specific event.
* <p>Warning: All calling methods must have first synchronized on this.</p>
* @param eventName The name of the event the listeners are listening to.
* @param inlineListeners Whether the listeners set returned is for the inlined listeners, versus the threaded listeners.
* @return The collection of listeners.
*/
private ListenerSets getOrCreateListeners(int eventNumber) {
ListenerSets result = (ListenerSets) threadedListenerMap.get(eventNumber);
if(result == null) {
result = new ListenerSets();
threadedListenerMap.put(eventNumber, result);
}//if//
return result;
}//getOrCreateListeners()//
/**
* Gets the object that we are supporting.
* @return The object this object is supporting.
*/
public IEventEmitter getSupportedObject() {
return supportedObject;
}//getSupportedObject()//
/**
* Determines whether there are listeners to a perticular event.
* @param event The event.
* @return Whether there are any event listeners.
*/
public boolean hasListeners(Event event) {
return hasListeners(event.getNumber());
}//hasListeners()//
/**
* Determines whether there are listeners to a perticular event.
* <p>Warning: This method is not synchronized since it is unlikly that making it synchronized is necessary. It is possible however that this thread's local cache of memory will be used instead of accessing the shared memory for the hashmap value.</p>
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event.
* @return Whether there are any event listeners.
*/
public boolean hasListeners(int eventNumber) {
return threadedListenerMap.get(eventNumber) != null;
}//hasListeners()//
/**
* Reads the event emitter support from a stream.
* @param in The input stream containing the support data.
*/
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
Class type = (Class) in.readObject();
int eventCount = in.readInt();
threadedListenerMap = new IntObjectHashMap(20);
while(eventCount-- > 0) {
String eventName = in.readUTF();
int eventNumber = getEventNumber(type, eventName);
int listenerCount = in.readInt();
ListenerSets listeners = getOrCreateListeners(eventNumber);
//Read the inlined listeners.//
while(listenerCount-- > 0) {
IEventHandler eventHandler = (IEventHandler) in.readObject();
//Check just in case there was an error serializing.//
if(eventHandler != null) {
listeners.addListener(eventHandler, true);
}//if//
}//while//
//Read the threaded listeners.//
listenerCount = in.readInt();
while(listenerCount-- > 0) {
IEventHandler eventHandler = (IEventHandler) in.readObject();
//Check just in case there was an error serializing.//
if(eventHandler != null) {
listeners.addListener(eventHandler, false);
}//if//
}//while//
}//while//
}//readExternal()//
/**
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param event The event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
*/
public void register(IEventEmitter eventEmitter, Event event, IEventHandler handler) {
register(eventEmitter, event.getNumber(), (IHandler) handler, false);
}//register()//
/**
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
* <p>This method should not usually be used directly.
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param event The event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @see #register(IEventEmitter, int, Handler)
* @see #register(IEventEmitter, int, IEventHandler)
*/
public void register(IEventEmitter eventEmitter, Event event, IHandler handler) {
register(eventEmitter, event.getNumber(), (IHandler) handler, false);
}//register()//
/**
* Registers a listener with an event emitter.
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param event The event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
*/
public void register(IEventEmitter eventEmitter, Event event, IEventHandler handler, boolean inline) {
register(eventEmitter, event.getNumber(), (IHandler) handler, inline);
}//register()//
/**
* Registers a listener with an event emitter.
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
* <p>This method should not usually be used directly.
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param event The event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
* @see #register(IEventEmitter, String, Handler, boolean)
* @see #register(IEventEmitter, String, IEventHandler, boolean)
*/
public void register(IEventEmitter eventEmitter, Event event, IHandler handler, boolean inline) {
register(eventEmitter, event.getNumber(), handler, inline);
}//register()//
/**
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
*/
public void register(IEventEmitter eventEmitter, int eventNumber, IEventHandler handler) {
register(eventEmitter, eventNumber, (IHandler) handler, false);
}//register()//
/**
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
* <p>This method should not usually be used directly.
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @see #register(IEventEmitter, int, Handler)
* @see #register(IEventEmitter, int, IEventHandler)
*/
public void register(IEventEmitter eventEmitter, int eventNumber, IHandler handler) {
register(eventEmitter, eventNumber, (IHandler) handler, false);
}//register()//
/**
* Registers a listener with an event emitter.
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register with.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
*/
public void register(IEventEmitter eventEmitter, int eventNumber, IEventHandler handler, boolean inline) {
register(eventEmitter, eventNumber, (IHandler) handler, inline);
}//register()//
/**
* Registers a listener with an event emitter.
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
* <p>This method should not usually be used directly.
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register.
* @param handler The handler to invoke when the event is fired by the event emitter.
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
* @see #register(IEventEmitter, String, Handler, boolean)
* @see #register(IEventEmitter, String, IEventHandler, boolean)
*/
public synchronized void register(IEventEmitter eventEmitter, int eventNumber, IHandler handler, boolean inline) {
IntObjectHashMap eventNameMap = getOrCreateEventMap(eventEmitter);
Object value = eventNameMap.get(eventNumber);
if(value == null) {
eventNameMap.put(eventNumber, handler);
}//if//
else if(value instanceof IHashSet) {
if(((IHashSet) value).add(handler) == -1) {
throw new IllegalArgumentException("Cannot register the same handler for the same event on the same event emitter. At least one of the three variables must be different.");
}//if//
}//else if//
else {
IHashSet set = new LiteHashSet(10);
set.add(value);
set.add(handler);
eventNameMap.put(eventNumber, set);
}//else//
eventEmitter.registerListener(eventNumber, Orb.isLocal(eventEmitter) ? handler : (IHandler) (handler instanceof IEventHandler ? Orb.getProxy(handler, IEventHandler.class) : Orb.getProxy(handler, IHandler.class)), inline);
}//register()//
/**
* Registers a listener to a perticular event.
* <p>Warning: This method is to be called by the event emitter. Use the register(..) methods if using the event support to register with an event emitter.</p>
* @param event The event to be listened to.
* @param eventHandler The handler to be called if the event is fired.
* @param inlined Whether the handler should be called using the same thread that fired the event, versus being called on the event thread in the order it was fired. For performance reasons this should generally be false.
*/
public void registerListener(Event event, IHandler eventHandler, boolean inlined) {
registerListener(event.getNumber(), eventHandler, inlined);
}//registerListener()//
/**
* Registers a listener to a perticular event.
* <p>Warning: This method is to be called by the event emitter. Use the register(..) methods if using the event support to register with an event emitter.</p>
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be listened to.
* @param eventHandler The handler to be called if the event is fired.
* @param inlined Whether the handler should be called using the same thread that fired the event, versus being called on the event thread in the order it was fired. For performance reasons this should generally be false.
*/
public synchronized void registerListener(int eventNumber, IHandler eventHandler, boolean inlined) {
ListenerSets listeners = getOrCreateListeners(eventNumber);
if(eventHandler == null) {
throw new NullPointerException("Null event handler passed when registering for an event with this event emitter.");
}//if//
listeners.addListener(eventHandler, inlined);
}//registerListener()//
/**
* Unregisters the specific event handler so it does not receive events from the given event emitter for the given event name.
* @param eventEmitter The event emitter to unregister with.
* @param event The event to be unregistered.
* @param eventHandler The specific handler to be unregistered.
*/
public void unregister(IEventEmitter eventEmitter, Event event, IHandler eventHandler) {
unregister(eventEmitter, event.getNumber(), eventHandler);
}//unregister()//
/**
* Unregisters the specific event handler so it does not receive events from the given event emitter for the given event name.
* @param eventEmitter The event emitter to unregister with.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be unregistered.
* @param eventHandler The specific handler to be unregistered.
*/
public synchronized void unregister(IEventEmitter eventEmitter, int eventNumber, IHandler eventHandler) {
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
if(eventMap != null) {
Object value = eventMap.get(eventNumber);
if(value instanceof IHashSet) {
((IHashSet) value).remove(eventHandler);
}//if//
else {
eventMap.remove(eventNumber);
}//else//
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, eventHandler);
}//if//
}//unregister()//
/**
* Unregisters all registered event handlers with all event emitters.
*/
public synchronized void unregisterAll() {
IIterator emitterIterator = emitterMap.keyIterator();
while(emitterIterator.hasNext()) {
IEventEmitter eventEmitter = (IEventEmitter) emitterIterator.next();
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
if(eventMap != null) {
IIntIterator eventIterator = eventMap.keyIterator();
while(eventIterator.hasNext()) {
int eventNumber = eventIterator.next();
Object value = eventMap.get(eventNumber);
if(value instanceof IHashSet) {
IIterator handlerIterator = ((IHashSet) value).iterator();
while(handlerIterator.hasNext()) {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
}//while//
((IHashSet) value).removeAll(); //Help GC.//
}//if//
else {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
}//else//
}//while//
eventMap.removeAll(); //Help GC.//
}//if//
}//while//
emitterMap.removeAll(); //Help GC.//
}//unregisterAll()//
/**
* Unregisters all registered event handlers for a single event emitter.
* @param eventEmitter The event emitter for which all registered events will be unregistered.
*/
public synchronized void unregisterAll(IEventEmitter eventEmitter) {
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.remove(eventEmitter);
if(eventMap != null) {
IIntIterator eventIterator = eventMap.keyIterator();
while(eventIterator.hasNext()) {
int eventNumber = eventIterator.next();
Object value = eventMap.get(eventNumber);
if(value instanceof IHashSet) {
IIterator handlerIterator = ((IHashSet) value).iterator();
while(handlerIterator.hasNext()) {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
}//while//
((IHashSet) value).removeAll(); //Help GC.//
}//if//
else {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
}//else//
}//while//
eventMap.removeAll(); //Help GC.//
}//if//
}//unregisterAll()//
/**
* Unregisters all registered event handlers for a single event emitter and a single event name.
* @param eventEmitter The event emitter for which all registered events will be unregistered.
* @param event The event for which all registered events will be unregistered.
*/
public void unregisterAll(IEventEmitter eventEmitter, Event event) {
unregisterAll(eventEmitter, event.getNumber());
}//unregisterAll()//
/**
* Unregisters all registered event handlers for a single event emitter and a single event name.
* @param eventEmitter The event emitter for which all registered events will be unregistered.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event for which all registered events will be unregistered.
*/
public synchronized void unregisterAll(IEventEmitter eventEmitter, int eventNumber) {
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
if(eventMap != null) {
Object value = eventMap.remove(eventNumber);
if(value instanceof IHashSet) {
IIterator handlerIterator = ((IHashSet) value).iterator();
while(handlerIterator.hasNext()) {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
}//while//
}//if//
else {
if(!Orb.isLocal(eventEmitter)) {
Orb.setOneWayCall(eventEmitter);
}//if//
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
}//else//
if(eventMap.getSize() == 0) {
emitterMap.remove(eventEmitter);
}//if//
}//if//
}//unregisterAll()//
/**
* Allows the IEventListener object to unregister it's event handlers.
* <p>Warning: This method is to be called by the event emitter. Use the unregister(..) methods if using the event support to unregister with an event emitter.</p>
* @param event The event whose listener will be unregistered.
* @param eventHandler The event handler that had been receiving event notifications.
*/
public void unregisterListener(Event event, IHandler eventHandler) {
unregisterListener(event.getNumber(), eventHandler);
}//unregisterListener()//
/**
* Allows the IEventListener object to unregister it's event handlers.
* <p>Warning: This method is to be called by the event emitter. Use the unregister(..) methods if using the event support to unregister with an event emitter.</p>
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event whose listener will be unregistered.
* @param eventHandler The event handler that had been receiving event notifications.
*/
public synchronized void unregisterListener(int eventNumber, IHandler eventHandler) {
ListenerSets listeners = getOrCreateListeners(eventNumber);
listeners.removeListener(eventHandler);
if(listeners.getListenerCount() == 0) {
threadedListenerMap.remove(eventNumber);
}//if//
}//unregisterListener()//
/* (non-Javadoc)
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public synchronized void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
Class type = supportedObject.getClass();
out.writeObject(type); //Note: The supported object class is required to convert the event names back to numbers.//
out.writeInt(threadedListenerMap.getSize());
IIntIterator iterator = threadedListenerMap.keyIterator();
while(iterator.hasNext()) {
int eventNumber = iterator.next();
ListenerSets listeners = (ListenerSets) threadedListenerMap.get(eventNumber);
IHashSet inlinedListeners = listeners.getListeners(true);
IHashSet threadedListeners = listeners.getListeners(false);
IIterator listenerIterator = inlinedListeners.iterator();
out.writeUTF(getEventName(type, eventNumber));
//Write out the inlined listeners.//
out.writeInt(inlinedListeners.getSize());
while(listenerIterator.hasNext()) {
IEventHandler listener = (IEventHandler) listenerIterator.next();
try {
out.writeObject(Orb.getProxy(listener, IEventHandler.class));
}//try//
catch(IllegalArgumentException e) {
Debug.log(e);
out.writeObject(null);
}//catch//
}//while//
//Write out the threaded listeners.//
out.writeInt(threadedListeners.getSize());
listenerIterator = threadedListeners.iterator();
while(listenerIterator.hasNext()) {
IEventHandler listener = (IEventHandler) listenerIterator.next();
try {
out.writeObject(Orb.getProxy(listener, IEventHandler.class));
}//try//
catch(IllegalArgumentException e) {
Debug.log(e);
out.writeObject(null);
}//catch//
}//while//
}//while//
}//writeExternal()//
}//EventEmitterSupport//

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
import com.foundation.metadata.Event;
public interface IEventEmitter {
/**
* Registers a listener with this event emitter for notification when an event is fired.
* @param eventNumber The number of the event to register with. Event numbers are either the attribute or custom event identifier defined by the declaring type.
* @param handler The handler that will be evaluated when ever the event is fired.
* @param inline Whether the handler call should be inlined such that the firing thread notifies the handler, versus an event processing thread notifying the handler in the order the event occured.
* @see #unregisterListener(int, IHandler)
*/
public void registerListener(int eventNumber, IHandler handler, boolean inline);
/**
* Unregisters a listener from this event emitter.
* @param eventNumber The number of the event to unregister from. Event numbers are either the attribute or custom event identifier defined by the declaring type.
* @param handler The handler that was orgininally registered.
* @see #registerListener(int, IHandler, boolean)
*/
public void unregisterListener(int eventNumber, IHandler handler);
/**
* Registers a listener with this event emitter for notification when an event is fired.
* @param eventNumber The number of the event to register with. Event numbers are either the attribute or custom event identifier defined by the declaring type.
* @param handler The handler that will be evaluated when ever the event is fired.
* @param inline Whether the handler call should be inlined such that the firing thread notifies the handler, versus an event processing thread notifying the handler in the order the event occured.
* @see #unregisterListener(int, IHandler)
*/
public void registerListener(Event eventNumber, IHandler handler, boolean inline);
/**
* Unregisters a listener from this event emitter.
* @param eventNumber The number of the event to unregister from. Event numbers are either the attribute or custom event identifier defined by the declaring type.
* @param handler The handler that was orgininally registered.
* @see #registerListener(int, IHandler, boolean)
*/
public void unregisterListener(Event eventNumber, IHandler handler);
}//IEventEmitter//

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
/**
* This interface defines a basic event handler that will allow an event handler to be passed remotely.
* <p>NOTE: This interface should never be used directly by an application.</p>
*/
public interface IEventHandler extends IHandler {
/**
* Does what ever the handler is programmed to do when called.
* @param eventEmitter The object that fired the event.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event being fired. This allows one handler to register for multiple events.
* @param parameters The parameters passed to the handler when it is invoked.
* @param flags The event dependant flags.
*/
public abstract void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags);
}//IEventHandler//

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
public interface IEventListener {
}//IEventListener//

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2002,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
/**
* This interface defines a basic event handler that will allow an event handler to be passed remotely.
* <p>NOTE: This interface should never be used directly by an application.</p>
*/
public interface IHandler {
/**
* Does what ever the handler is programmed to do when called.
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event being fired. This allows one handler to register for multiple events.
* @param parameters The parameters passed to the handler when it is invoked.
* @param flags A set of flags that depend on the event type. These flags are event dependant.
*/
public void evaluate(int eventNumber, Object[] eventParameters, int flags);
}//IHandler//

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2005,2008 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
import com.common.thread.*;
/**
* Defines the methods of a request handler which processes requests in sequential order on a single request thread.
*/
public interface IRequestHandler {
/**
* Determines whether the calling thread is the thread that handles requests.
* @return Whether the calling thread is the request thread.
*/
public boolean isRequestThread();
/**
* Executes a runnable through the request thread. This method allows single threading of all requests.
* @param runnable The runnable to be executed.
* @return The result of the runnable.
*/
public Object execute(IRunnable runnable);
/**
* Executes a runnable through the request thread without waiting for the response. This method allows single threading of all requests.
* If the calling thread is the request thread, the runnable will not be run immediatly, instead it will be run at some point in the future when this thread finishes its current task and releases control.
* @param runnable The runnable to be executed.
*/
public void executeAsync(IRunnable runnable);
/**
* Executes a runnable through the event thread. This method allows single threading of an entire view.
* @param runnable The runnable to be executed.
* @param synchronous Whether the thread should block until the request has been processed.
* @return The result of the runnable, or null if asynchronous.
*/
public Object execute(IRunnable runnable, boolean synchronous);
/**
* Determines whether the event loop is activly running.
* @return Whether the loop is active.
*/
public boolean isRunning();
}//IRequestHandler//

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2005,2007 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event;
import com.common.util.IIterator;
import com.common.util.LiteHashSet;
/*
* Extends the LiteHashSet to provide efficient retreival of a set of listeners conforming to a specific class.
*/
public class ListenerSet extends LiteHashSet {
public static class ListenerSetIterator extends LiteHashSet.HashSetIterator {
private Class listenerClass = null;
/**
* ListenerSetIterator constructor.
* @param hashSet The set of all listeners.
* @param listenerClass The the class that the resulting listeners must implement/extend.
*/
protected ListenerSetIterator(LiteHashSet hashSet, Class listenerClass) {
super(hashSet);
this.listenerClass = listenerClass;
}//ListenerSetIterator()//
/* (non-Javadoc)
* @see com.common.util.LiteHashSet.HashSetIterator#loadNextEntry()
*/
protected void loadNextEntry() {
super.loadNextEntry();
//Skip next values which don't conform.//
while(peekHasNext() && (!(listenerClass.isAssignableFrom(peekNext().getClass())))) {
currentEntry = nextEntry;
currentIndex = nextIndex;
hasNextEntry = false;
nextEntry = null;
}//while//
}//loadNextEntry()//
}//ListenerSetIterator//
/**
* ListenerSet constructor.
* @param initialCapacity
*/
public ListenerSet(int initialCapacity) {
super(initialCapacity);
}//ListenerSet()//
/* (non-Javadoc)
* @see com.common.util.LiteHashSet#iterator()
*/
public IIterator iterator(Class listenerClass) {
return new ListenerSetIterator(this, listenerClass);
}//iterator()//
}//ListenerSet//

View File

@@ -0,0 +1,405 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import com.common.comparison.Comparator;
import com.common.debug.Debug;
import com.common.util.*;
import com.foundation.common.IEntity;
import com.foundation.event.*;
public abstract class Binding {
public static final int FLAG_EXCLUDE_SUBCLASSES = 0x01;
/** The listener the binding is associated with. */
private ModelListener listener = null;
/** The support object that can be used to register for events. */
private EventSupport eventSupport = null;
/** The type of entity which this binding applies to. */
private Class entityType = null;
/** The flags assigned to this binding. */
private int flags = 0;
/** A set of metadata objects. Existing metadata for an object can be found by creating a new Metadata with the object and Binding references and then performing a get on this collection passing the new Metadata. Metadata is matched via the object and the source binding using logical equality. */
private LiteHashSet metadataSet = new LiteHashSet(10, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getLogicalComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
/**
* Extends the weak reference to provide access to the binding that is using the weak reference.
*/
protected static class ObjectReference extends WeakReference {
/** The binding metadata that is using the reference. This is required to allow the listener to release the binding when the weak reference is invalidated. */
private Metadata metadata = null;
/**
* ObjectReference constructor.
* @param object The object being referenced weakly.
* @param metadata The binding metadata that is using the reference. This is required to allow the listener to release the binding when the weak reference is invalidated.
* @param queue The reference queue used to determine when the reference is no longer valid.
*/
public ObjectReference(Object object, Metadata metadata, ReferenceQueue queue) {
super(object, queue);
this.metadata = metadata;
}//ObjectReference()//
/**
* Gets the binding metadata that is using the reference.
* @return The binding metdata that will be useable to cleanup when the reference is invalidated.
*/
public Metadata getMetadata() {
return metadata;
}//getMetadata()//
}//ObjectReference//
/**
* Metadata about the registered object in this binding.
* <p>This object can be compared to either another Metadata or to the object its metadata describes and the comparison will be deemed equal if the objects are exactly equal.</p>
*/
protected static abstract class Metadata {
/** The weak reference to the object the metadata describes. */
private ObjectReference objectRef = null;
/** The strong reference to the object the metadata describes. If this is null then the weak reference must not be null. */
private Object object;
/** Counts the number of times the entity has been registered with this binding. */
private int counter = 0;
/** A reference to the binding that created this entity metadata. */
private Binding binding;
/** The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener, or if there are multiple sources. */
private Metadata source;
/** The hash code for this metadata object. */
private int hashCode = 0;
/**
* Metadata constructor.
* @param binding The binding that created this metadata.
* @param source The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener.
* @param object The object the metadata describes.
*/
public Metadata(Binding binding, Metadata source, Object object) {
if(binding == null) {
throw new IllegalArgumentException("Must provide a non-null binding.");
}//if//
if(object == null) {
throw new IllegalArgumentException("Must provide a non-null object.");
}//if//
this.binding = binding;
this.source = source;
this.object = object;
this.hashCode = object.hashCode() ^ (source == null ? 0 : source.hashCode());
}//Metadata()//
/**
* Converts the metadata's object reference into a weak reference to allow the VM to GC the referenced object when memory is tight.
* @param referenceQueue The queue used to release the binding if the object is moved out of memory.
*/
protected void makeWeak(ReferenceQueue referenceQueue) {
this.objectRef = new ObjectReference(object, this, referenceQueue);
this.object = null;
}//makeWeak()//
/**
* Gets the single source EntityMetadata for this entity.
* The value may be null if the entity is a root, but it may also be null if the entity is no longer referenced.
* @return The source EntityMetadata for the entity, or null if the entity is a root, or null if there are multiple sources for the same entity (in which case getSources returns a non-null value).
*/
protected Metadata getSource() {
return source;
}//getSource()//
/**
* Determines whether collection notification is required for the given collection depth level.
* @param collectionDepth The level of depth of the collection. A zero depth collection is one that is not contained in another collection.
* @param listeners The set of listeners that are interested in the collection change.
*/
protected IModelListenerCollectionHandler getCollectionModelListener(int collectionDepth) {
return null;
}//getCollectionModelListener()//
/**
* Searches for the nearest binding metadata whose object matches the given type.
* @param type The type of object being searched for. This can be null in which case the all objects will match and the first will be returned.
* @param allowSubclasses Whether to allow subclasses of the type.
* @return The first matching object, or null.
*/
public Object getObject(Class type, boolean allowSubclasses) {
Object result = (type == null) || (allowSubclasses && type.isAssignableFrom(getObject().getClass()) || (type.equals(getObject().getClass()))) ? getObject() : null;
return result == null ? (getSource() != null ? getSource().getObject(type, allowSubclasses) : null) : result;
}//getObject()//
/**
* Gets the binding for this entity metadata.
* @return The metadata's binding which created this metadata.
*/
public Binding getBinding() {
return binding;
}//getBinding()//
/**
* Gets the object that this metadata describes.
* @return The object wrappered by this metadata.
*/
public Object getObject() {
return object == null ? objectRef.get() : object;
}//getObject()//
/**
* Gets the number of times the entity is referenced by the visible model.
* @return The count of references to the entity.
*/
private int getCounter() {
return counter;
}//getCounter()//
/**
* Sets the number of times the entity is referenced by the visible model.
* @param counter The count of references to the entity.
*/
private void setCounter(int counter) {
this.counter = counter;
}//setCounter()//
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public final boolean equals(Object metadata) {
Object object = getObject();
return metadata instanceof Metadata ? object == null ? metadata == this : (object == ((Metadata) metadata).getObject() && getSource() == ((Metadata) metadata).getSource()) : false;
}//equals()//
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public final int hashCode() {
return hashCode;
}//hashCode()//
/**
* Some simple cleanup code that helps the GC thread.
*/
protected void release() {
binding = null;
object = null;
objectRef = null;
source = null;
}//release()//
}//Metadata//
/**
* Binding constructor.
* @param listener The listener that this binding is associated with.
* @param entityType The type of entity that this binding applies to.
* @param flags The applicable flags for this binding, or zero.
*/
public Binding(Class entityType, int flags) {
this.entityType = entityType;
this.flags = flags;
}//Binding()//
/**
* Gets the model listener this binding is attached to.
* @return The attached model listener.
*/
public ModelListener getListener() {
return listener;
}//getListener()//
/**
* Sets the listener this binding is being used by.
* @param listener The binding's listener.
*/
void setListener(ModelListener listener) {
if(this.listener == null) {
this.listener = listener;
this.eventSupport = listener.getEventSupport();
}//if//
else {
//Can't associate the binding with more than one listener.//
throw new IllegalArgumentException();
}//else//
}//setListener()//
/**
* Gets the optional handlers called when the attribute value is a collection whose values are changed.
* @return Array of collection changed handlers used to notify if the attribute's value is a collection (or collection of collections, etc). The depth of the collection that changed is the index into this array.
*/
protected IModelListenerCollectionHandler[] getCollectionHandlers() {
return null;
}//getCollectionHandlers()//
/**
* Gets the class of entity that this binding applies to.
* @return The class of entity the binding applies to.
*/
public Class getEntityType() {
return entityType;
}//getEntityType()//
/**
* Gets the event support usable by this binding to register for events.
* @return The support object usable for event registration.
*/
protected EventSupport getEventSupport() {
return eventSupport;
}//getEventSupport()//
/**
* Gets the flags for the binding.
* @return The binding's flags.
*/
protected int getFlags() {
return flags;
}//getFlags()//
/**
* Determines whether this binding should not include subclasses of the entity type.
* @return Whether subclasses of the entity type don't match this binding.
*/
public boolean excludeSubclasses() {
return (flags & FLAG_EXCLUDE_SUBCLASSES) > 0;
}//excludeSubclasses()//
/**
* Determines whether this binding applies to the given entity.
* <p>Subclasses should most likely not override this method.</p>
* @param entity The entity that may match this binding.
* @param isFirstMatch Whether there was a previous match.
* @return Whether this binding can service the entity.
*/
public boolean matches(IEntity entity, boolean isFirstMatch) {
return excludeSubclasses() ? getEntityType().equals(entity.getClass()) : getEntityType().isAssignableFrom(entity.getClass());
}//matches()//
/**
* Gets the existing metadata for the given query metadata.
* @param query The metadata used to find the existing metadata. This should have assigned the source and value.
* @return The existing metadata or null if it doesn't exist.
*/
protected Metadata getMetadata(Metadata query) {
return (Metadata) metadataSet.get(query);
}//getMetadata()//
/**
* Gets the metadata for the given value and source.
* @param value The value that the metadata represents.
* @param source The source indicating where the value came from (which binding created it), or null if it is a root value.
* @return The metadata for the value and source, or null if the binding doesn't know of the value.
*/
protected abstract Metadata getMetadata(Object value, Metadata source);
/**
* Registers the entity with this binding.
* @param source The EntityMetadata that generated the entity reference, or null if the entity is root to this listener.
* @param entity The entity being registered.
* @param handler The optional collection change handler that if provided will be notified when collection changes occur.
* @param referenceQueue The reference queue used to cleanup after bindings to objects no longer in memory.
* @param flags The flags for the operation, or zero.
* @return The metadata that was created as a result of the register. This will be null if the register simply incremented the reference count.
*/
public Metadata register(Metadata source, Object object, ReferenceQueue referenceQueue, int flags) {
Metadata metadata = internalCreateMetadata(source, object);
//If the metadata already exists (will only be true for collections since they could reference the same object twice) then just increment the counter.//
if(metadataSet.containsValue(metadata)) {
metadata = (Metadata) metadataSet.get(metadata);
metadata.setCounter(metadata.getCounter() + 1);
metadata = null;
}//if//
else {
metadata.makeWeak(referenceQueue);
metadata.setCounter(metadata.getCounter() + 1);
metadataSet.add(metadata);
}//else//
return metadata;
}//register()//
/**
* Unregisters the entity with this binding.
* @param source The EntityMetadata that generated the entity reference, or null if the entity is root to this listener.
* @param entity The entity being unregistered.
* @param flags The flags for the operation, or zero.
*/
public void unregister(Metadata source, Object object, int flags) {
Metadata metadata = internalCreateMetadata(source, object);
//If the metadata exists in our set then get it and either reduce the counter or remove it altogether.//
if(metadataSet.containsValue(metadata)) {
metadata = (Metadata) metadataSet.get(metadata);
if(metadata.getCounter() != 1) {
metadata.setCounter(metadata.getCounter() - 1);
}//if//
else {
metadataSet.remove(metadata);
internalRelease(metadata, flags);
metadata.release();
}//else//
}//if//
else {
//TODO: What to do about this? The value was never added.
Debug.log(new RuntimeException("Cannot remove what wasn't added."));
}//else//
}//unregister()//
/**
* Unregisters the entity with this binding.
* @param metadata The metadata that should be removed. This may be provided in place of the object reference and source when a weakly referenced object is removed from memory.
*/
public void unregister(Metadata metadata) {
metadataSet.remove(metadata);
internalRelease(metadata, 0);
metadata.release();
}//unregister()//
/**
* Creates a new (uninitialized) metadata object for the given object and source.
* @param source The source of the new metadata object.
* @param object The object the metadata will describe.
* @return The uninitialized metadata for the object. If the metadata isn't used it will be GC'd, otherwise the internalRegister(Metadata, Metadata) method will be called.
*/
protected abstract Metadata internalCreateMetadata(Metadata source, Object object);
/**
* Initializes the metadata to be used by this binding & notifies the listeners that the metadata's object exists.
* @param metadata The metadata for the object being initialized in the context of this binding.
* @param flags The flags for the operation, or zero.
*/
protected abstract void internalInitialize(Metadata metadata, int flags);
/**
* Releases the metadata no longer used by this binding & notifies the listeners that the metadata's object no longer exists.
* @param metadata The metadata for the object being released in the context of this binding.
* @param flags The flags for the operation, or zero.
*/
protected abstract void internalRelease(Metadata metadata, int flags);
/**
* Called when the binding finds a new value that may not be part of the listener's scope yet.
* @param source The source for the value.
* @param value The found value.
* @param flags The flags for the operation, or zero.
*/
protected void valueAdded(Metadata source, Object value, int flags) {
listener.valueAdded(source, value, flags);
}//valueAdded()//
/**
* Called when the binding no longer references the previously found value.
* @param source The source for the value.
* @param value The value that may no longer be in the listener's scope.
* @param flags The flags for the operation, or zero.
*/
protected void valueRemoved(Metadata source, Object value, int flags) {
listener.valueRemoved(source, value, flags);
}//valueRemoved()//
/**
* Provides the binding an opportunity to perform pre-change processing.
*/
protected void changeStarted() {
listener.changeStarted();
}//changeStarted()//
/**
* Provides the binding an opportunity to perform post-change processing.
*/
protected void changeStopped() {
listener.changeStopped();
}//changeStopped()//
/**
* Generates debug output for the binding.
* @param buffer The debug output buffer.
* @param source The source for the value.
* @param value The found value.
* @param tabDepth The number of tab indents.
* @param handler The optional handler used to generate human readable strings for referenced values.
*/
protected void debug(StringBuffer buffer, Metadata source, Object value, int tabDepth, ModelListener.DebugHandler handler) {
listener.debug(buffer, value, source, tabDepth, handler);
}//debug()//
/**
* Generates debug output for the binding.
* @param buffer The debug output buffer.
* @param value The found value.
* @param tabDepth The number of tab indents.
* @param handler The optional handler used to generate human readable strings for referenced values.
*/
protected abstract void debug(StringBuffer buffer, Binding.Metadata metadata, int tabDepth, ModelListener.DebugHandler handler);
}//Binding//

View File

@@ -0,0 +1,277 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
import com.common.util.*;
import com.foundation.util.IInlineCollectionObservable;
import com.foundation.util.IInlineCollectionObserver;
public class CollectionBinding extends Binding {
protected static class CollectionMetadata extends Binding.Metadata implements IInlineCollectionObserver {
/** A temporary handler reference which is used to notify the listener of a set of changes. */
private IModelListenerCollectionHandler handler = null;
/** A temporary added values collection which is used to notify the listener of a set of changes. */
private IList addedValues = null;
/** A temporary removed values collection which is used to notify the listener of a set of changes. */
private IList removedValues = null;
/** This is normally zero but may be non-zero while the listener is initializing. It is assumed that the collection is locked such that durring initialization only the one thread can make collection changes. */
private int flags = 0;
/** Whether the changes should be held so they can be dealt with in bulk when the stopChanges method is called. */
private boolean holdChanges = false;
/**
* CollectionMetadata constructor.
* @param binding The binding that created this metadata.
* @param source The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener.
* @param collection The collection the metadata describes.
*/
public CollectionMetadata(Binding binding, Binding.Metadata source, ICollection collection) {
super(binding, source, collection);
}//CollectionMetadata()//
/* (non-Javadoc)
* @see com.foundation.common.Binding.Metadata#getCollectionModelListener(int)
*/
protected IModelListenerCollectionHandler getCollectionModelListener(int collectionDepth) {
return getSource().getCollectionModelListener(collectionDepth + 1);
}//getCollectionModelListener()//
/*
* Collects the collection model listeners for this collection.
* @param listeners The set of unique listeners for this collection.
*
public IModelListenerCollectionHandler getCollectionModelListener() {
return getSource() != null ? getSource().getCollectionModelListener(0) : null;
}//getCollectionModelListener()//
*/
/**
* Gets the handler notified if the collection is altered.
* @return The handler to be notified of changes.
*/
public IModelListenerCollectionHandler getHandler() {
return handler;
}//getHandler()//
/**
* Sets the handler notified if the collection is altered.
* @param handler The handler to be notified of changes.
*/
public void setHandler(IModelListenerCollectionHandler handler) {
this.handler = handler;
}//setHandler()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#startChanges(int)
*/
public void startChanges(int changeCount) {
holdChanges = true;
//If there is a listener then prepare to save the adds & removes.//
if(handler != null) {
addedValues = new LiteList(changeCount > 0 ? changeCount : 100);
removedValues = new LiteList(changeCount > 0 ? changeCount : 100);
}//if//
getBinding().changeStarted();
}//startChanges()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#stopChanges()
*/
public void stopChanges() {
holdChanges = false;
//Notify the handler of the changes.//
if(handler != null) {
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
handler.run(getObject(handler.getReferenceType(), true), handlerFlags, addedValues, removedValues);
//Cleanup the added/removed collections.//
addedValues.removeAll();
addedValues = null;
removedValues.removeAll();
removedValues = null;
}//while//
//Notify the binding the change stopped.//
getBinding().changeStopped();
}//stopChanges()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#valueAdded(java.lang.Object)
*/
public void valueAdded(Object value) {
if(addedValues != null) {
addedValues.add(value);
}//if//
getBinding().valueAdded(this, value, flags);
//Notify the handler of the changes.//
if(!holdChanges && handler != null) {
handler.run(getObject(handler.getReferenceType(), true), 0, new LiteList((Object) value), LiteList.EMPTY_LIST);
}//if//
}//valueAdded()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#valueRemoved(java.lang.Object)
*/
public void valueRemoved(Object value) {
if(removedValues != null) {
removedValues.add(value);
}//if//
getBinding().valueRemoved(this, value, flags);
//Notify the handler of the changes.//
if(!holdChanges && handler != null) {
handler.run(getObject(handler.getReferenceType(), true), 0, LiteList.EMPTY_LIST, new LiteList((Object) value));
}//if//
}//valueRemoved()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObserver#allRemoved(com.common.util.IList)
*/
public void removingAll() {
LiteList removed = new LiteList((ICollection) getObject());
if(removedValues != null) {
removedValues.addAll(removed);
}//if//
for(int index = 0; index < removed.getSize(); index++) {
getBinding().valueRemoved(this, removed.get(index), flags);
}//for//
//Notify the handler of the changes.//
if(!holdChanges && handler != null) {
//Note that this method is called PRIOR to a removeAll, so the actual collection will still have its values at the time of this line's execution.//
handler.run(getObject(handler.getReferenceType(), true), 0, LiteList.EMPTY_LIST, removed);
}//if//
}//removingAll()//
}//AttributeEntityMetadata//
/**
* Determines whether the event is occuring while the model listener is in the process of initializing.
* @param flags The flags from the IModelListenerAttributeHandler.
* @return Whether the listener is initializing.
*/
public static boolean isListenerInitializing(int flags) {
return (flags & IModelListenerCollectionHandler.FLAG_LISTENER_INITIALIZING) > 0;
}//isListenerInitializing()//
/**
* Determines whether the event is occuring while the model listener is in the process of releasing.
* @param flags The flags from the IModelListenerAttributeHandler.
* @return Whether the listener is releasing.
*/
public static boolean isListenerReleasing(int flags) {
return (flags & IModelListenerCollectionHandler.FLAG_LISTENER_RELEASING) > 0;
}//isListenerReleasing()//
/**
* CollectionBinding constructor.
* @param listener The listener that this binding is associated with.
*/
CollectionBinding(ModelListener listener) {
super(null, 0);
setListener(listener);
}//CollectionBinding()//
/* (non-Javadoc)
* @see com.foundation.common.Binding#internalCreateMetadata(com.foundation.common.Binding.Metadata, java.lang.Object)
*/
protected Metadata internalCreateMetadata(Metadata source, Object object) {
return new CollectionMetadata(this, source, (ICollection) object);
}//internalCreateMetadata()//
/* (non-Javadoc)
* @see com.foundation.event.model.Binding#internalInitialize(com.foundation.event.model.Binding.Metadata, int)
*/
protected void internalInitialize(Binding.Metadata metadata, int flags) {
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
ICollection collection = (ICollection) collectionMetadata.getObject();
IModelListenerCollectionHandler handler = collectionMetadata.getHandler();
//Start listening for collection changes. Note: This generates add events which are captured by the CollectionMetadata and which will add the value to the listener scope.//
if(collection instanceof IInlineCollectionObservable) {
collectionMetadata.flags = flags;
((IInlineCollectionObservable) collection).addCollectionObserver(collectionMetadata);
//If the collection has no elements then the add observer will optimize and not notify the listener. We need the listener notified regardless of the element count, so do so now.//
if(collection.getSize() == 0 && handler != null) {
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, LiteList.EMPTY_LIST, LiteList.EMPTY_LIST);
}//if//
collectionMetadata.flags = 0;
}//if//
else {
IList values = new LiteList(collection, 200, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
//Add the collection values to the listener.//
for(int index = 0; index < values.getSize(); index++) {
valueAdded(collectionMetadata, values.get(index), flags);
}//for//
//If there is a listener then notify of the new collection values.//
if(handler != null) {
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, values, LiteList.EMPTY_LIST);
}//if//
}//else//
}//internalInitialize()//
/* (non-Javadoc)
* @see com.foundation.event.model.Binding#internalRelease(com.foundation.event.model.Binding.Metadata, int)
*/
protected void internalRelease(Binding.Metadata metadata, int flags) {
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
ICollection collection = (ICollection) metadata.getObject();
IModelListenerCollectionHandler handler = collectionMetadata.getHandler();
IList values = new LiteList(collection, 200, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
//Stop listening for collection changes. Note: Unlike the addCollectionObserver(..) call, this does not generate remove events.//
if(collection instanceof IInlineCollectionObservable) {
((IInlineCollectionObservable) collection).removeCollectionObserver((CollectionMetadata) metadata);
}//if//
//Remove the collection values from the listener.//
for(int index = 0; index < values.getSize(); index++) {
valueRemoved(metadata, values.get(index), flags);
}//for//
//If there is a listener then notify of the removed collection values.//
if(handler != null) {
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_RELEASING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_RELEASING : 0;
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, values, LiteList.EMPTY_LIST);
}//if//
}//internalRelease()//
/* (non-Javadoc)
* @see com.foundation.event.model.Binding#getMetadata(java.lang.Object, com.foundation.event.model.Binding)
*/
protected Metadata getMetadata(Object value, Metadata source) {
return getMetadata(new CollectionMetadata(this, source, (ICollection) value));
}//getMetadata()//
/* (non-Javadoc)
* @see com.foundation.event.model.Binding#debug(java.lang.StringBuffer, com.foundation.event.model.Binding.Metadata, int, com.foundation.event.model.ModelListener.DebugHandler)
*/
protected void debug(StringBuffer buffer, Binding.Metadata metadata, int tabDepth, ModelListener.DebugHandler handler) {
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
ICollection collection = (ICollection) collectionMetadata.getObject();
IList values = new LiteList(collection, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
for(int index = 0; index < tabDepth; index++) {
buffer.append('\t');
}//for//
buffer.append("Collection {");
buffer.append("\r\n");
//Navigate to the value's bindings.//
for(int index = 0; index < values.getSize(); index++) {
debug(buffer, collectionMetadata, values.get(index), tabDepth + 1, handler);
}//for//
for(int index = 0; index < tabDepth; index++) {
buffer.append('\t');
}//for//
buffer.append("}");
buffer.append("\r\n");
}//debug()//
}//CollectionBinding//

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
public interface IModelListenerAttributeHandler extends IModelListenerHandler {
/** This type indicates that a the attribute's parent was added to the model listener's scope and thus while the attribute's value may not have changed, the listener may still be interested in knowing it now exists. */
public static final int TYPE_ADDED = 0;
/** This type indicates that the attribute's parent was removed from the model listener's scope and thus while the attribute's value may not have changed, the listener may still be interested in knowing it isn't part of the scope any more. */
public static final int TYPE_REMOVED = 1;
/** This type indicates that the attribute's value has changed due to it being lazily initialized. The old value will always be NOT_SET. */
public static final int TYPE_INITIALIZED = 2;
/** This type indicates that the attribute's value has been changed while the model listener was observing it. This is the most common event type. */
public static final int TYPE_CHANGED = 3;
/**
* This flag is only used in conjunction with the TYPE_ADDED or TYPE_REMOVED types and it indicates that the newValue or oldValue will be NOT_SET because the attribute's value has not been initialized.
* This is never set if the AttributeBinding was created with the FLAG_LAZY_LOAD_ATTRIBUTE flag set.
* @see AttributeBinding.FLAG_LAZY_LOAD_ATTRIBUTE
* @see #TYPE_ADDED
* @see #TYPE_REMOVED
*/
public static final int FLAG_ATTRIBUTE_UNINITIALIZED = 0x01;
/** This flag is set if the model listener is in the process of initializing. During the listener's initializing a lot of TYPE_ADDED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
public static final int FLAG_LISTENER_INITIALIZING = 0x02;
/** This flag is set if the model listener is in the process of releasing. During the listener's releasing a lot of TYPE_REMOVED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
public static final int FLAG_LISTENER_RELEASING = 0x04;
/** This object is passed in place of the old or new value parameters if the attribute is or has not been set yet (it has not yet been initialized to some value). */
public static final Object NOT_SET = new Object();
/**
* Notifies the runnable that the event being listened for occured.
* <p><b>Warning: This handler is called inline by the thread that is performing the changes. The thread has (if coded correctly) locked the collection's monitor.</b></p>
* @param object The object of the type returned by getReferenceType(), or null if an object of this type was not found by the listener in the hierarchy for the event.
* @param type One of the type codes defined by this listener class. This can either be TYPE_REMOVED, TYPE_ADDED, or TYPE_CHANGED indicating that the attribute changed, or the path to the attribute was removed, or the path to the attribute is new.
* @param flags Zero or more of the flags identifiers.
* @param oldValue The previous value of the attribute, only valid if a single attribute is being listened to by this handler. This may be the constant NOT_SET if the attribute was not previously assigned a value.
* @param newValue The new value of the attribute, only valid if a single attribute is being listened to by this handler. This may be the constant NOT_SET if the type is TYPE_ADDED and the attribute has not yet been assigned a value.
*/
public void run(Object object, int type, int flags, Object oldValue, Object newValue);
}//IModelListenerAttributeHandler//

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
/**
* Used by the model listener to notify listeners when anything changes.
* No notification of exactly what changed since that would be quite difficult.
* Notification is always performed after all the changes have been dealt with so that calls to the handler are minimized.
* <p>Note: Notification is provided on a different thread than the one that caused the change. This is done to prevent deadlocks if the calling thread holds locks on a part of the model.</p>
*/
public interface IModelListenerChangeHandler extends Runnable {
}//IModelListenerChangeHandler//

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
import com.common.util.IList;
public interface IModelListenerCollectionHandler extends IModelListenerHandler {
/** This flag is set if the model listener is in the process of initializing. During the listener's initializing a lot of TYPE_ADDED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
public static final int FLAG_LISTENER_INITIALIZING = 0x02;
/** This flag is set if the model listener is in the process of releasing. During the listener's releasing a lot of TYPE_REMOVED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
public static final int FLAG_LISTENER_RELEASING = 0x04;
/**
* Notifies the runnable that the event being listened for occured.
* <p><b>Warning: This handler is called inline by the thread that is performing the changes. The thread has (if coded correctly) locked the collection's monitor.</b></p>
* @param object The object of the type returned by getReferenceType(), or null if an object of this type was not found by the listener in the hierarchy for the event.
* @param flags Zero or more of the flags identifiers.
* @param addedValues The values added to the collection. This is also called if a collection is added to the model listener's scope.
* @param removedValues The values removed from the collection. This is also called if a collection is removed from the model listener's scope.
*/
public void run(Object object, int flags, IList addedValues, IList removedValues);
}//IModelListenerCollectionHandler//

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
public interface IModelListenerHandler {
/**
* Gets the type of object that the listener should pass to the handler when invoked.
* <p>Scenario: The listener is listening to a collection of Project objects and each project's name attribute.
* The name attribute's binding specifies a handler which wants to be notified when a project is added, removed, or the name changes.
* This method would return Project.class so that the handler is passed a reference to the project whose name changed, got added, or was removed.</p>
* @return The type of object reference to be passed to the handler when invoked, or null if the nearest object should be passed.
*/
public Class getReferenceType();
}//IModelListenerHandler//

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2006,2008 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.event.model;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import com.common.thread.Monitor;
import com.common.thread.Scheduler;
import com.common.thread.ThreadService;
import com.common.util.*;
import com.foundation.common.IEntity;
import com.foundation.event.*;
import com.foundation.metadata.ISupportsContainment;
/**
* A model listener is often used to listen to changes in a model object or in a hierarchy of model objects.
* It is capable of digging arbitrarily deep into the model based on the bindings provided.
* A listener can be initialized and released when no longer needed, or it can be initialized with the model and allowed to exist as long as the model exists (the model must retain a reference to the listener otherwise GC will have its way).
* All listener/model references are weak references and thus will not keep the model in memory.
* The bindings for an attribute will not cause the attribute to be loaded if it has not yet been realized.
* <p>
* To use the model listener, the application must:
* <ol>
* <li>Create a new ModelListener passing the initial value or values to be listened to, and an optional change hander that will be called any time any binding is altered in any way.
* <li>Create and add new bindings. Currently there is only one binding type useable which is AttributeBinding which takes an IEntity class, an attribute defined by the class, an optional flags (usually zero), an optional change handler (usually used to get attribute change notification), and an optional array of change handlers used only if the attribute's value is a collection.
* <li>Call the initialize method on the ModelListener.
* <li>Save a reference to the ModelListener so it doesn't get GC'd.
* <li>Optionally release the ModelListener when it is no longer needed. GC will take care of this if the reference to the ModelListener is lost.
* </ol>
*/
public class ModelListener {
public static final int INTERNAL_FLAG_INITIALIZING = 1;
public static final int INTERNAL_FLAG_RELEASING = 2;
/** The collection of bindings that are listened to. */
private IList bindings = new LiteList(5, 15);
/** The collection binding that the listener uses specially for ICollection values. */
private CollectionBinding collectionBinding = new CollectionBinding(this);
/** Whether the listener has been initialized. Once initialized the listener begins to listen for changes, bindings cannot be changed and listeners cannot be added, and must be released to avoid memory leaks. */
private volatile boolean isInitialized = false;
/** The event support that will manage the event registration for this listener. */
private EventSupport eventSupport = new EventSupport(null);
/** The root object that starts the listener off. This is null if the listener has a parent listener. */
private Object rootObject = null;
/** The optional listener to be notified when any change occurs in the model. */
private IModelListenerChangeHandler changeHandler = null;
/** A counter used to determine when a set of changes has been completed. */
private int changeStartCount = 0;
/** Whether to inline the calls to the global change handler. */
private boolean inlineGlobalChangeHandler = false;
/** The reference queue used to quickly clean up after metadata that references objects no longer in memory. */
private static final ReferenceQueue referenceQueue = new ReferenceQueue();
//Setup the scheduled task to cleanup the reference queue for all model listeners.//
static {
Scheduler.addTask(10000, new CleanupTask(referenceQueue), false);
}//static//
/**
* The cleanup task used to release bindings that reference values no longer in memory.
*/
private static class CleanupTask extends Scheduler.Task {
private ReferenceQueue queue = null;
/**
* CleanupTask constructor.
* @param listener The model listener which will be weakly referenced so that the listener can be GC'd and this task will know to be released.
* @param queue The reference queue to be listening to.
*/
public CleanupTask(ReferenceQueue queue) {
super();
this.queue = queue;
}//CleanupTask()//
/* (non-Javadoc)
* @see com.common.thread.Scheduler.Task#evaluate()
*/
public void evaluate() {
Binding.ObjectReference reference = null;
//Remove all bindings for objects no longer in memory.//
while((reference = (Binding.ObjectReference) queue.poll()) != null) {
reference.getMetadata().getBinding().unregister(reference.getMetadata());
}//while//
}//evaluate()//
}//CleanupTask//
/**
* Used by the debug code to create human readable strings for values referenced by the model listener.
*/
public static abstract class DebugHandler {
public abstract String toString(Object value);
public String defaultToString(Object value) {
return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode());
}//defaultToString()//
}//IDebugHandler//
/**
* ModelListener constructor.
* @param entity The entity that is the root object for the listener.
*/
public ModelListener(IEntity entity) {
this((Object) entity, null, false);
}//ModelListener()//
/**
* ModelListener constructor.
* @param entity The entity that is the root object for the listener.
* @param changeHandler The optional handler called if any binding in the listener is updated.
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
*/
public ModelListener(IEntity entity, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
this((Object) entity, changeHandler, inlineGlobalChangeHandler);
}//ModelListener()//
/**
* ModelListener constructor.
* @param collection The collection that is the root object for the listener.
*/
public ModelListener(ICollection collection) {
this((Object) collection, null, false);
}//ModelListener()//
/**
* ModelListener constructor.
* @param collection The collection that is the root object for the listener.
* @param changeHandler The optional handler called if any binding in the listener is updated.
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
*/
public ModelListener(ICollection collection, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
this((Object) collection, changeHandler, inlineGlobalChangeHandler);
}//ModelListener()//
/**
* ModelListener constructor.
* @param rootObject The object which is used by the listener as the initial value.
* @param changeHandler The optional handler called if any binding in the listener is updated.
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
*/
private ModelListener(Object rootObject, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
this.rootObject = rootObject;
this.changeHandler = changeHandler;
this.inlineGlobalChangeHandler = inlineGlobalChangeHandler;
}//ModelListener()//
/**
* Gets whether the listener has been initialized yet.
* @return The initialization state for this listener.
*/
public boolean isInitialized() {
return isInitialized;
}//isInitialized()//
/**
* Gets the event support used by the listener and all its bindings.
* @return The shared event support object used by bindings to detect changes in the model.
*/
protected EventSupport getEventSupport() {
return eventSupport;
}//getEventSupport()//
/**
* Called by the bindings when they detect a change.
*/
protected void changeStarted() {
if(changeHandler != null) {
internalChangeStarted();
}//if//
}//changeStarted()//
/**
* Notifies the listener when a change to the model has completed.
*/
protected void changeStopped() {
if(changeHandler != null) {
internalChangeStopped();
}//if//
}//changeStopped()//
/**
* Called by the bindings when they detect a change.
*/
private synchronized void internalChangeStarted() {
changeStartCount++;
}//internalChangeStarted()//
/**
* Notifies the listener when a change to the model has completed.
*/
private synchronized void internalChangeStopped() {
changeStartCount--;
if(changeStartCount == 0) {
if(inlineGlobalChangeHandler) {
changeHandler.run();
}//if//
else {
ThreadService.run(changeHandler);
}//else//
}//if//
}//internalChangeStopped()//
/**
* Adds an attribute binding.
* <p>Note: This is not thread safe since the same thread that created this listener should be the one to initialize it prior to making is shared.</p>
* @param binding The binding to be added to this listener.
*/
public void addBinding(Binding binding) {
if(!isInitialized) {
binding.setListener(this);
bindings.add(binding);
}//if//
else {
//Cannot add bindings after the listener has been initialized.//
throw new IllegalArgumentException();
}//else//
}//addBinding()//
/**
* Initializes the listener once the listener has been completely setup.
* Once initialized the bindings may not be changed.
* After initialization, the listeners will be attached to the data and the specified listener runnables will be called when changes occur.
*/
public void initialize() {
if((rootObject != null) && (!isInitialized)) {
isInitialized = true;
changeStarted();
valueAdded(null, rootObject, INTERNAL_FLAG_INITIALIZING);
changeStopped();
}//if//
else {
throw new RuntimeException("Cannot initialize what has already been initialized.");
}//else//
}//initialize()//
/**
* Releases the listeners attached to the data. Once released this listener can be re-initialized.
* <p>
* Note: It is not required that the release method be called.
* This is provided to allow a listener that isn't required any more to be released.
* If the listener is no longer referenced then it will be released automatically when the GC thread gets to it.
* </p>
*/
public void release() {
if((rootObject != null) && (isInitialized)) {
changeStarted();
valueRemoved(null, rootObject, INTERNAL_FLAG_RELEASING);
changeStopped();
isInitialized = false;
}//if//
else {
throw new RuntimeException("Cannot release what has not been initialized.");
}//else//
}//release()//
/**
* Called when a value enters the scope of this listener.
* @param source The metadata for the entity which produced the value.
* @param value The value to listen to and whose subcomponents may become values in this listener's scope.
* @param flags The flags for the operation, or zero.
*/
void valueAdded(Binding.Metadata source, Object value, int flags) {
if(value != null) {
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
boolean hasBinding = false;
try {
if(value instanceof ICollection) {
CollectionBinding.CollectionMetadata collectionMetadata;
//Lock on the new monitor if possible.//
if(monitor != null) {
hasBinding = true;
Monitor.lock(monitor);
}//if//
//Treat collections specially since there is only one binding we are interested in (the collection values).//
collectionMetadata = (CollectionBinding.CollectionMetadata) collectionBinding.register(source, value, referenceQueue, flags);
if(collectionMetadata != null) {
int index = 0;
Binding.Metadata nextSource = source;
IModelListenerCollectionHandler[] handlers;
//Get the right collection handler for the source and value.//
while(nextSource instanceof CollectionBinding.CollectionMetadata) {
nextSource = nextSource.getSource();
index++;
}//while//
handlers = nextSource == null ? null : nextSource.getBinding().getCollectionHandlers();
collectionMetadata.setHandler(handlers != null && handlers.length > index ? handlers[index] : null);
collectionBinding.internalInitialize(collectionMetadata, flags);
}//if//
}//if//
else if(value instanceof IEntity) {
IEntity entity = (IEntity) value;
//Apply the entity to any bindings it matches.//
for(int index = 0; index < bindings.getSize(); index++) {
Binding binding = (Binding) bindings.get(index);
if(binding.matches(entity, hasBinding)) {
Binding.Metadata bindingMetadata;
//If this is the first binding we have found then set the flag and check the locks.//
if(!hasBinding) {
hasBinding = true;
//Lock on the new monitor if possible.//
if(monitor != null) {
Monitor.lock(monitor);
}//if//
}//if//
bindingMetadata = binding.register(source, entity, referenceQueue, flags);
if(bindingMetadata != null) {
binding.internalInitialize(bindingMetadata, flags);
}//if//
}//if//
}//for//
}//else//
}//try//
finally {
//Re-lock on the old monitor if necessary.//
if((hasBinding) && (monitor != null)) {
Monitor.unlock(monitor);
}//if//
}//finally//
}//if//
}//valueAdded()//
/**
* Called when a value is removed from the scope of this listener.
* @param source The metadata for the entity which produced the value.
* @param value The value to stop listening on and remove from the scope of this listener. All subcomponents will also no longer be in this listener's scope.
* @param flags The flags for the operation, or zero.
*/
void valueRemoved(Binding.Metadata source, Object value, int flags) {
if(value != null) {
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
boolean hasBinding = false;
if(value instanceof ICollection) {
//Lock on the new monitor if possible.//
if(monitor != null) {
Monitor.lock(monitor);
}//if//
//Treat collections specially since there is only one binding we are interested in (the collection values).//
collectionBinding.unregister(source, value, flags);
hasBinding = true;
}//if//
else if(value instanceof IEntity) {
IEntity entity = (IEntity) value;
//Apply the entity to any bindings it matches.//
for(int index = 0; index < bindings.getSize(); index++) {
Binding binding = (Binding) bindings.get(index);
if(binding.matches(entity, hasBinding)) {
//If this is the first binding we have found then set the flag and check the locks.//
if(!hasBinding) {
hasBinding = true;
//Lock on the new monitor if possible.//
if(monitor != null) {
Monitor.lock(monitor);
}//if//
}//if//
binding.unregister(source, entity, flags);
}//if//
}//for//
}//else//
//Re-lock on the old monitor if necessary.//
if((hasBinding) && (monitor != null)) {
Monitor.unlock(monitor);
}//if//
}//if//
}//valueRemoved()//
/**
* Generates a debug string for the model listener's registered models.
* @param buffer The output buffer.
* @param handler The optional handler used to generate human readable strings from objects referenced by the model listener.
* @param initialTabDepth The initial depth to tab the resulting string to.
*/
public void debug(StringBuffer buffer, DebugHandler handler, int initialTabDepth) {
debug(buffer, rootObject, null, initialTabDepth, handler == null ? new DebugHandler() {
public String toString(Object value) {
return defaultToString(value);
}//toString()//
} : handler);
}//debug()//
/**
* Generates a debug string for the model listener's registered models.
* @param buffer The buffer to use for display.
* @param value The value to display and whose bindings should be navigated.
* @param source The value's binding source, or null for root objects.
* @param tabDepth The depth of the tab indents.
* @param handler The optional handler used to generate human readable strings from objects referenced by the model listener.
*/
protected void debug(StringBuffer buffer, Object value, Binding.Metadata source, int tabDepth, DebugHandler handler) {
if(value != null) {
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
boolean hasBinding = false;
if(value instanceof ICollection) {
CollectionBinding.CollectionMetadata collectionMetadata;
//Lock on the new monitor if possible.//
if(monitor != null) {
Monitor.lock(monitor);
}//if//
collectionMetadata = (CollectionBinding.CollectionMetadata) collectionBinding.getMetadata(value, source);
if(collectionMetadata != null) {
collectionBinding.debug(buffer, collectionMetadata, tabDepth, handler);
}//if//
hasBinding = true;
}//if//
else if(value instanceof IEntity) {
IEntity entity = (IEntity) value;
for(int index = 0; index < tabDepth; index++) {
buffer.append('\t');
}//for//
buffer.append(handler.toString(entity));
buffer.append("\r\n");
//Apply the entity to any bindings it matches.//
for(int index = 0; index < bindings.getSize(); index++) {
Binding binding = (Binding) bindings.get(index);
if(binding.matches(entity, hasBinding)) {
Binding.Metadata bindingMetadata;
//If this is the first binding we have found then set the flag and check the locks.//
if(!hasBinding) {
hasBinding = true;
//Lock on the new monitor if possible.//
if(monitor != null) {
Monitor.lock(monitor);
}//if//
}//if//
bindingMetadata = binding.getMetadata(entity, source);
if(bindingMetadata != null) {
binding.debug(buffer, bindingMetadata, tabDepth + 1, handler);
}//if//
}//if//
}//for//
}//else//
//Re-lock on the old monitor if necessary.//
if((hasBinding) && (monitor != null)) {
Monitor.unlock(monitor);
}//if//
}//if//
}//debug()//
}//ModelListener//