ICUNotifier.java
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* 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;
/**
* 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>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. Attempts 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() {
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.
*/
@Override
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);
}