/** ******************************************************************************* * 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.

* *

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 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(); } 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 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 queue = new ArrayList(); 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); }