2 *******************************************************************************
3 * Copyright (C) 2001-2009, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.util.ArrayList;
10 import java.util.EventListener;
11 import java.util.Iterator;
12 import java.util.List;
15 * <p>Abstract implementation of a notification facility. Clients add
16 * EventListeners with addListener and remove them with removeListener.
17 * Notifiers call notifyChanged when they wish to notify listeners.
18 * This queues the listener list on the notification thread, which
19 * eventually dequeues the list and calls notifyListener on each
20 * listener in the list.</p>
22 * <p>Subclasses override acceptsListener and notifyListener
23 * to add type-safe notification. AcceptsListener should return
24 * true if the listener is of the appropriate type; ICUNotifier
25 * itself will ensure the listener is non-null and that the
26 * identical listener is not already registered with the Notifier.
27 * NotifyListener should cast the listener to the appropriate
28 * type and call the appropriate method on the listener.
30 public abstract class ICUNotifier {
31 private final Object notifyLock = new Object();
32 private NotifyThread notifyThread;
33 private List<EventListener> listeners;
36 * Add a listener to be notified when notifyChanged is called.
37 * The listener must not be null. AcceptsListener must return
38 * true for the listener. Attempts to concurrently
39 * register the identical listener more than once will be
42 public void addListener(EventListener l) {
44 throw new NullPointerException();
47 if (acceptsListener(l)) {
48 synchronized (notifyLock) {
49 if (listeners == null) {
50 listeners = new ArrayList<EventListener>();
52 // identity equality check
53 for (EventListener ll : listeners) {
63 throw new IllegalStateException("Listener invalid for this notifier.");
68 * Stop notifying this listener. The listener must
69 * not be null. Attemps to remove a listener that is
70 * not registered will be silently ignored.
72 public void removeListener(EventListener l) {
74 throw new NullPointerException();
76 synchronized (notifyLock) {
77 if (listeners != null) {
78 // identity equality check
79 Iterator<EventListener> iter = listeners.iterator();
80 while (iter.hasNext()) {
81 if (iter.next() == l) {
83 if (listeners.size() == 0) {
94 * Queue a notification on the notification thread for the current
95 * listeners. When the thread unqueues the notification, notifyListener
96 * is called on each listener from the notification thread.
98 public void notifyChanged() {
99 if (listeners != null) {
100 synchronized (notifyLock) {
101 if (listeners != null) {
102 if (notifyThread == null) {
103 notifyThread = new NotifyThread(this);
104 notifyThread.setDaemon(true);
105 notifyThread.start();
107 notifyThread.queue(listeners.toArray(new EventListener[listeners.size()]));
114 * The notification thread.
116 private static class NotifyThread extends Thread {
117 private final ICUNotifier notifier;
118 private final List<EventListener[]> queue = new ArrayList<EventListener[]>();
120 NotifyThread(ICUNotifier notifier) {
121 this.notifier = notifier;
125 * Queue the notification on the thread.
127 public void queue(EventListener[] list) {
128 synchronized (this) {
135 * Wait for a notification to be queued, then notify all
136 * listeners listed in the notification.
139 EventListener[] list;
142 synchronized (this) {
143 while (queue.isEmpty()) {
146 list = queue.remove(0);
149 for (int i = 0; i < list.length; ++i) {
150 notifier.notifyListener(list[i]);
153 catch (InterruptedException e) {
160 * Subclasses implement this to return true if the listener is
161 * of the appropriate type.
163 protected abstract boolean acceptsListener(EventListener l);
166 * Subclasses implement this to notify the listener.
168 protected abstract void notifyListener(EventListener l);