]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/ICUNotifier.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / ICUNotifier.java
1 /**\r
2  *******************************************************************************\r
3  * Copyright (C) 2001-2009, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.impl;\r
8 \r
9 import java.util.ArrayList;\r
10 import java.util.EventListener;\r
11 import java.util.Iterator;\r
12 import java.util.List;\r
13 \r
14 /**\r
15  * <p>Abstract implementation of a notification facility.  Clients add\r
16  * EventListeners with addListener and remove them with removeListener.\r
17  * Notifiers call notifyChanged when they wish to notify listeners.\r
18  * This queues the listener list on the notification thread, which\r
19  * eventually dequeues the list and calls notifyListener on each\r
20  * listener in the list.</p>\r
21  *\r
22  * <p>Subclasses override acceptsListener and notifyListener \r
23  * to add type-safe notification.  AcceptsListener should return\r
24  * true if the listener is of the appropriate type; ICUNotifier\r
25  * itself will ensure the listener is non-null and that the\r
26  * identical listener is not already registered with the Notifier.\r
27  * NotifyListener should cast the listener to the appropriate \r
28  * type and call the appropriate method on the listener.\r
29  */\r
30 public abstract class ICUNotifier {\r
31     private final Object notifyLock = new Object();\r
32     private NotifyThread notifyThread;\r
33     private List<EventListener> listeners;\r
34 \r
35     /**\r
36      * Add a listener to be notified when notifyChanged is called.\r
37      * The listener must not be null. AcceptsListener must return\r
38      * true for the listener.  Attempts to concurrently\r
39      * register the identical listener more than once will be\r
40      * silently ignored.  \r
41      */\r
42     public void addListener(EventListener l) {\r
43         if (l == null) {\r
44             throw new NullPointerException();\r
45         }\r
46 \r
47         if (acceptsListener(l)) {\r
48             synchronized (notifyLock) {\r
49                 if (listeners == null) {\r
50                     listeners = new ArrayList<EventListener>();\r
51                 } else {\r
52                     // identity equality check\r
53                     for (EventListener ll : listeners) {\r
54                         if (ll == l) {\r
55                             return;\r
56                         }\r
57                     }\r
58                 }\r
59 \r
60                 listeners.add(l);\r
61             }\r
62         } else {\r
63             throw new IllegalStateException("Listener invalid for this notifier.");\r
64         }\r
65     }\r
66 \r
67     /**\r
68      * Stop notifying this listener.  The listener must\r
69      * not be null.  Attemps to remove a listener that is\r
70      * not registered will be silently ignored.\r
71      */\r
72     public void removeListener(EventListener l) {\r
73         if (l == null) {\r
74             throw new NullPointerException();\r
75         }\r
76         synchronized (notifyLock) {\r
77             if (listeners != null) {\r
78                 // identity equality check\r
79                 Iterator<EventListener> iter = listeners.iterator();\r
80                 while (iter.hasNext()) {\r
81                     if (iter.next() == l) {\r
82                         iter.remove();\r
83                         if (listeners.size() == 0) {\r
84                             listeners = null;\r
85                         }\r
86                         return;\r
87                     }\r
88                 }\r
89             }\r
90         }\r
91     }\r
92 \r
93     /**\r
94      * Queue a notification on the notification thread for the current\r
95      * listeners.  When the thread unqueues the notification, notifyListener\r
96      * is called on each listener from the notification thread.\r
97      */\r
98     public void notifyChanged() {\r
99         if (listeners != null) {\r
100             synchronized (notifyLock) {\r
101                 if (listeners != null) {\r
102                     if (notifyThread == null) {\r
103                         notifyThread = new NotifyThread(this);\r
104                         notifyThread.setDaemon(true);\r
105                         notifyThread.start();\r
106                     }\r
107                     notifyThread.queue(listeners.toArray(new EventListener[listeners.size()]));\r
108                 }\r
109             }\r
110         }\r
111     }\r
112 \r
113     /**\r
114      * The notification thread.\r
115      */\r
116     private static class NotifyThread extends Thread {\r
117         private final ICUNotifier notifier;\r
118         private final List<EventListener[]> queue = new ArrayList<EventListener[]>();\r
119 \r
120         NotifyThread(ICUNotifier notifier) {\r
121             this.notifier = notifier;\r
122         }\r
123 \r
124         /**\r
125          * Queue the notification on the thread.\r
126          */\r
127         public void queue(EventListener[] list) {\r
128             synchronized (this) {\r
129                 queue.add(list);\r
130                 notify();\r
131             }\r
132         }\r
133 \r
134         /**\r
135          * Wait for a notification to be queued, then notify all\r
136          * listeners listed in the notification.\r
137          */\r
138         public void run() {\r
139             EventListener[] list;\r
140             while (true) {\r
141                 try {\r
142                     synchronized (this) {\r
143                         while (queue.isEmpty()) {\r
144                             wait();\r
145                         }\r
146                         list = queue.remove(0);\r
147                     }\r
148 \r
149                     for (int i = 0; i < list.length; ++i) {\r
150                         notifier.notifyListener(list[i]);\r
151                     }\r
152                 }\r
153                 catch (InterruptedException e) {\r
154                 }\r
155             }\r
156         }\r
157     }\r
158 \r
159     /**\r
160      * Subclasses implement this to return true if the listener is\r
161      * of the appropriate type.\r
162      */\r
163     protected abstract boolean acceptsListener(EventListener l);\r
164 \r
165     /**\r
166      * Subclasses implement this to notify the listener.\r
167      */\r
168     protected abstract void notifyListener(EventListener l);\r
169 }\r