001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.impl;
007:
008: import java.util.ArrayList;
009: import java.util.EventListener;
010: import java.util.Iterator;
011: import java.util.LinkedList;
012: import java.util.List;
013:
014: /**
015: * <p>Abstract implementation of a notification facility. Clients add
016: * EventListeners with addListener and remove them with removeListener.
017: * Notifiers call notifyChanged when they wish to notify listeners.
018: * This queues the listener list on the notification thread, which
019: * eventually dequeues the list and calls notifyListener on each
020: * listener in the list.</p>
021: *
022: * <p>Subclasses override acceptsListener and notifyListener
023: * to add type-safe notification. AcceptsListener should return
024: * true if the listener is of the appropriate type; ICUNotifier
025: * itself will ensure the listener is non-null and that the
026: * identical listener is not already registered with the Notifier.
027: * NotifyListener should cast the listener to the appropriate
028: * type and call the appropriate method on the listener.
029: */
030: public abstract class ICUNotifier {
031: private final Object notifyLock = new Object();
032: private NotifyThread notifyThread;
033: private List listeners;
034:
035: /**
036: * Add a listener to be notified when notifyChanged is called.
037: * The listener must not be null. AcceptsListener must return
038: * true for the listener. Attempts to concurrently
039: * register the identical listener more than once will be
040: * silently ignored.
041: */
042: public void addListener(EventListener l) {
043: if (l == null) {
044: throw new NullPointerException();
045: }
046:
047: if (acceptsListener(l)) {
048: synchronized (notifyLock) {
049: if (listeners == null) {
050: listeners = new ArrayList(5);
051: } else {
052: // identity equality check
053: Iterator iter = listeners.iterator();
054: while (iter.hasNext()) {
055: if (iter.next() == l) {
056: return;
057: }
058: }
059: }
060:
061: listeners.add(l);
062: }
063: } else {
064: throw new IllegalStateException(
065: "Listener invalid for this notifier.");
066: }
067: }
068:
069: /**
070: * Stop notifying this listener. The listener must
071: * not be null. Attemps to remove a listener that is
072: * not registered will be silently ignored.
073: */
074: public void removeListener(EventListener l) {
075: if (l == null) {
076: throw new NullPointerException();
077: }
078: synchronized (notifyLock) {
079: if (listeners != null) {
080: // identity equality check
081: Iterator iter = listeners.iterator();
082: while (iter.hasNext()) {
083: if (iter.next() == l) {
084: iter.remove();
085: if (listeners.size() == 0) {
086: listeners = null;
087: }
088: return;
089: }
090: }
091: }
092: }
093: }
094:
095: /**
096: * Queue a notification on the notification thread for the current
097: * listeners. When the thread unqueues the notification, notifyListener
098: * is called on each listener from the notification thread.
099: */
100: public void notifyChanged() {
101: if (listeners != null) {
102: synchronized (notifyLock) {
103: if (listeners != null) {
104: if (notifyThread == null) {
105: notifyThread = new NotifyThread(this );
106: notifyThread.setDaemon(true);
107: notifyThread.start();
108: }
109: notifyThread.queue(listeners.toArray());
110: }
111: }
112: }
113: }
114:
115: /**
116: * The notification thread.
117: */
118: private static class NotifyThread extends Thread {
119: private final ICUNotifier notifier;
120: private final List queue = new LinkedList();
121:
122: NotifyThread(ICUNotifier notifier) {
123: this .notifier = notifier;
124: }
125:
126: /**
127: * Queue the notification on the thread.
128: */
129: public void queue(Object[] list) {
130: synchronized (this ) {
131: queue.add(list);
132: notify();
133: }
134: }
135:
136: /**
137: * Wait for a notification to be queued, then notify all
138: * listeners listed in the notification.
139: */
140: public void run() {
141: Object[] list;
142: while (true) {
143: try {
144: synchronized (this ) {
145: while (queue.isEmpty()) {
146: wait();
147: }
148: list = (Object[]) queue.remove(0);
149: }
150:
151: for (int i = 0; i < list.length; ++i) {
152: notifier
153: .notifyListener((EventListener) list[i]);
154: }
155: } catch (InterruptedException e) {
156: }
157: }
158: }
159: }
160:
161: /**
162: * Subclasses implement this to return true if the listener is
163: * of the appropriate type.
164: */
165: protected abstract boolean acceptsListener(EventListener l);
166:
167: /**
168: * Subclasses implement this to notify the listener.
169: */
170: protected abstract void notifyListener(EventListener l);
171: }
|