The Event Subsystem
Arbitrary named
Events can be passed around Spiegel between
EventSources and
EventListeners.
Events have four fields:
source,
type,
name, and
value. The
source is a reference to the
EventSource object that triggered the event. The
type is a mixed-case string indicating the type of event that occurred.
Name and
value are optional fields used to indicate more specifically the nature of the event, or to make it more convenient for the listener to access the affected data.
An object producing events must implement the
EventSource interface. This interface provides methods for adding and removing listeners. In most cases the actual work can be offloaded to the
EventManager class, which will store the listeners and send events to all applicable listeners upon request. Listeners can be set for events of only a certain type, by passing the string type name as the type parameter, or they can receive events of all types triggered by the object by passing
null for the type. A typical use is as follows:
class Thing implements EventSource {
private EventManager eventManager;
public Thing() {
eventManager = new EventManager(this);
}
public void addListener(String type, EventListener listener) {
eventManager.addListener(type, listener);
}
public void removeListener(String type, EventListener listener) {
eventManager.removeListener(type, listener);
}
public void doSomething() {
eventManager.fireEvent("somethingHappened", "name", "value");
}
}
Listeners implement the
EventListener interface, which defines the
eventOccurred() method. A sample implementation follows:
class ThingListener implements EventListener {
public void eventOccurred(Event e) {
System.out.println("Event " + e.getType() + " from " + e.getSource() +
": name = " + e.getName() + ", value = " + e.getValue());
}
}
All events normally enter an
EventListener through one method, where their
type must then be examined to determine what is to be done with them. To alleviate the need for manual sorting of events, the
EventDispatcher object automatically finds and calls a method with a name derived from the event. For example,
EventDispatcher would handle an event from a
Function of type
Stars and an event type
setName by calling the method
onStarsSetName in the target object. If this method is not found, it will then try the names of the source class's superclasses. If
Object is reached, the class name is omitted from the method name entirely. The
Stars class extends
Function, so the next method attempted is
onFunctionSetName. Failing that, it tries
onSetName (in lieu of
onObjectSetName). In any case, the method must accept a single argument of type
Event. An example:
class Listener {
public Listener(Stars stars) {
stars.addListener("setName", new EventDispatcher(this));
}
public void onStarsSetName(Event e) {
// ...
}
}
If an object wishes to listen for events from another object and pass them on as its own, the
EventRelay class is useful. It is an
EventListener, but rather than handling the events itself, it changes the source field in the event to a given
EventSource and then fires the event through that object's
EventManager. All listeners on that object will receive the events as if they originated directly from that object. Example usage:
class CameraView {
private EventManager eventManager;
public CameraView(Camera camera) {
eventManager = new EventManager(this);
camera.addListener("moveto", new EventRelay(eventManager));
}
}
This example listens for "moveto" events coming from the
Camera object, and re-fires them with the same type, name, and value, but with a different source, the
CameraView. This allows other objects to listen for
CameraView events if it is inconvenient to listen on the
Camera directly.
EventRelay also accepts a string argument to its constructor, allowing the event type to be changed as well as the source.
--
TimPeterson - 12 Jun 2006