blob: 90c9206c1026c5c662dd0821d86db89bcc336a4d [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 2001-2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.Iterator;
import java.util.List;
/**
* <p>Abstract implementation of a notification facility. Clients add
* EventListeners with addListener and remove them with removeListener.
* Notifiers call notifyChanged when they wish to notify listeners.
* This queues the listener list on the notification thread, which
* eventually dequeues the list and calls notifyListener on each
* listener in the list.</p>
*
* <p>Subclasses override acceptsListener and notifyListener
* to add type-safe notification. AcceptsListener should return
* true if the listener is of the appropriate type; ICUNotifier
* itself will ensure the listener is non-null and that the
* identical listener is not already registered with the Notifier.
* NotifyListener should cast the listener to the appropriate
* type and call the appropriate method on the listener.
*/
public abstract class ICUNotifier {
private final Object notifyLock = new Object();
private NotifyThread notifyThread;
private List<EventListener> listeners;
/**
* Add a listener to be notified when notifyChanged is called.
* The listener must not be null. AcceptsListener must return
* true for the listener. Attempts to concurrently
* register the identical listener more than once will be
* silently ignored.
*/
public void addListener(EventListener l) {
if (l == null) {
throw new NullPointerException();
}
if (acceptsListener(l)) {
synchronized (notifyLock) {
if (listeners == null) {
listeners = new ArrayList<EventListener>();
} else {
// identity equality check
for (EventListener ll : listeners) {
if (ll == l) {
return;
}
}
}
listeners.add(l);
}
} else {
throw new IllegalStateException("Listener invalid for this notifier.");
}
}
/**
* Stop notifying this listener. The listener must
* not be null. Attemps to remove a listener that is
* not registered will be silently ignored.
*/
public void removeListener(EventListener l) {
if (l == null) {
throw new NullPointerException();
}
synchronized (notifyLock) {
if (listeners != null) {
// identity equality check
Iterator<EventListener> iter = listeners.iterator();
while (iter.hasNext()) {
if (iter.next() == l) {
iter.remove();
if (listeners.size() == 0) {
listeners = null;
}
return;
}
}
}
}
}
/**
* Queue a notification on the notification thread for the current
* listeners. When the thread unqueues the notification, notifyListener
* is called on each listener from the notification thread.
*/
public void notifyChanged() {
if (listeners != null) {
synchronized (notifyLock) {
if (listeners != null) {
if (notifyThread == null) {
notifyThread = new NotifyThread(this);
notifyThread.setDaemon(true);
notifyThread.start();
}
notifyThread.queue(listeners.toArray(new EventListener[listeners.size()]));
}
}
}
}
/**
* The notification thread.
*/
private static class NotifyThread extends Thread {
private final ICUNotifier notifier;
private final List<EventListener[]> queue = new ArrayList<EventListener[]>();
NotifyThread(ICUNotifier notifier) {
this.notifier = notifier;
}
/**
* Queue the notification on the thread.
*/
public void queue(EventListener[] list) {
synchronized (this) {
queue.add(list);
notify();
}
}
/**
* Wait for a notification to be queued, then notify all
* listeners listed in the notification.
*/
public void run() {
EventListener[] list;
while (true) {
try {
synchronized (this) {
while (queue.isEmpty()) {
wait();
}
list = queue.remove(0);
}
for (int i = 0; i < list.length; ++i) {
notifier.notifyListener(list[i]);
}
}
catch (InterruptedException e) {
}
}
}
}
/**
* Subclasses implement this to return true if the listener is
* of the appropriate type.
*/
protected abstract boolean acceptsListener(EventListener l);
/**
* Subclasses implement this to notify the listener.
*/
protected abstract void notifyListener(EventListener l);
}