upstream version 1.2.2
[debian/freetts] / com / sun / speech / engine / SpeechEventUtilities.java
1 /**
2  * Copyright 1998-2001 Sun Microsystems, Inc.
3  * 
4  * See the file "license.terms" for information on usage and
5  * redistribution of this file, and for a DISCLAIMER OF ALL 
6  * WARRANTIES.
7  */
8 package com.sun.speech.engine;
9
10 import javax.speech.SpeechEvent;
11
12 import java.awt.AWTEvent;
13 import java.awt.EventQueue;
14 import java.awt.Toolkit;
15 import java.awt.Component;
16 import java.security.AccessControlException;
17
18 /**
19  * Utilities to help with dispatch JSAPI 1.0 events on the event
20  * dispatching thread of AWT/Swing.  This is needed to help
21  * applications conform with the Swing Event Thread model.  If these
22  * utilities were not used, then a GUI application would have to
23  * implement Runnables to handle JSAPI events that result in updates
24  * to the GUI.
25  */
26 public class SpeechEventUtilities {
27
28     /**
29      * If true, the AWT EventQueue has been set up in the VM.  This flag
30      * is used to determine whether we should use the AWT EventQueue for
31      * synchronizing SpeechEvents with the AWT EventQueue or not.
32      */
33     protected static boolean awtRunning = false;
34     
35     /**
36      * The AWT EventQueue.  This is lazily created in postSpeechEvent to
37      * delay the need to initialize the Toolkit until it is necessary.
38      *
39      * @see #postSpeechEvent
40      */
41     protected static EventQueue systemEventQueue = null;
42
43     /**
44      * A target used to process speechAWTEvent objects.  This target
45      * is a component that expresses interest in SpeechAWTEvents.  It
46      * is lazily created along with systemEventQueue in postSpeechEvent.
47      *
48      * @see #postSpeechEvent
49      */
50     protected static SpeechAWTEventTarget speechAWTEventTarget = null;
51
52     /**
53      * If true, wait until an event has been dispatched before returning
54      * from the post method.  This is meant to be a global debugging flag.
55      * If a class calling postSpeechEvent wants to wait until the
56      * SpeechEvent has been dispatched, it should call the postSpeechEvent
57      * method that has the waitUntilDispatched parameter.
58      *
59      * @see #postSpeechEvent
60      */
61     public static boolean waitUntilDispatched = false;
62
63     /**
64      * Determine if the AWT event queue is running.  This method is one big
65      * hack, and we will be entering a bug against AWT to provide us with
66      * a real method for determining if AWT is active or not.  The problem
67      * with asking AWT if it is active right now is that it will activate
68      * it if it isn't already active.
69      */
70     static protected boolean isAWTRunning() {
71          
72         if (awtRunning) {
73             return true;
74         }
75  
76         try {
77             ThreadGroup rootGroup;
78             ThreadGroup parent;
79             ThreadGroup g = Thread.currentThread().getThreadGroup();
80             rootGroup = g;
81             parent = rootGroup.getParent();
82             while (parent != null) {
83                 rootGroup = parent;
84                 parent = parent.getParent();
85             }
86
87             int activeCount = rootGroup.activeCount();
88             Thread[] threads = new Thread[activeCount];
89             rootGroup.enumerate(threads,true);
90             for (int i = 0; i < threads.length; i++) {
91                 if (threads[i] != null) {
92                     String name = threads[i].getName();
93                     if (name.startsWith("AWT-EventQueue")) {
94                         awtRunning = true;
95                         return true;
96                     }
97                 }
98             }
99         } catch (AccessControlException ace) {
100             // if we receive an access control exception then
101             // it is likely that we are running in an applet
102             // in which case AWT is running. 
103             // I'm not sure if this is always true, perhaps
104             // there is another way to tell if we are running in an
105             // applet.
106
107             return true;
108         }
109
110         return false;        
111     }
112
113     /**
114      * Post a JSAPI SpeechEvent.  This is to be used by multiple processes
115      * to synchronize SpeechEvents.  It currently uses the AWT EventQueue
116      * as a means for doing this, which has the added benefit of causing
117      * all SpeechEvent notification to be done from the event dispatch
118      * thread.  This is important because the Swing Thread Model requires
119      * all interaction with Swing components to be done from the event
120      * dispatch thread.
121      *
122      * This method will immediately return once the event has been
123      * posted if the global waitUntilDispatched flag is set to false.
124      * Otherwise, it will wait until the event has been dispatched
125      * before returning.
126      *
127      * @param dispatcher the dispatcher that will dispatch the event
128      * @param event the SpeechEvent to post
129      */
130     static public void postSpeechEvent(SpeechEventDispatcher dispatcher,
131                                        SpeechEvent           event) {
132         postSpeechEvent(dispatcher, event, waitUntilDispatched);
133     }
134
135     /**
136      * Post a JSAPI SpeechEvent.  This is to be used by multiple processes
137      * to synchronize SpeechEvents.  It currently uses the AWT EventQueue
138      * as a means for doing this, which has the added benefit of causing
139      * all SpeechEvent notification to be done from the event dispatch
140      * thread.  This is important because the Swing Thread Model requires
141      * all interaction with Swing components to be done from the event
142      * dispatch thread.
143      *
144      * This method will immediately return once the event has been
145      * posted if the waitUntilDispatched parameter is set to false.
146      * Otherwise, it will wait until the event has been dispatched
147      * before returning.
148      *
149      * @param dispatcher the dispatcher that will dispatch the event
150      * @param event the SpeechEvent to post
151      * @param waitUntilDispatched if true, do not return until the
152      * event have been dispatched
153      */
154     static public void postSpeechEvent(
155         SpeechEventDispatcher dispatcher,
156         SpeechEvent           event,
157         boolean               waitUntilDispatched) {
158
159         /* Only use the AWT EventQueue if AWT is running.  If it isn't
160          * running, then just call the dispatcher directly.  A more formal
161          * event queue mechanism probably should be added at some point
162          * so listeners cannot cause a hang in the engine.
163          */
164         if (isAWTRunning()) {
165             /* Create the event target and event queue references if they
166              * haven't been created yet.  This is done here to delay the
167              * initialization of the AWT Toolkit until it is absolutely
168              * necessary.  Creating it earlier may cause conflicts with
169              * applets.
170              */
171             if (speechAWTEventTarget == null) {
172                 speechAWTEventTarget = new SpeechAWTEventTarget();
173                 systemEventQueue =
174                     Toolkit.getDefaultToolkit().getSystemEventQueue();
175             }
176
177             /* Post the event to the AWT EventQueue.  When AWT dispatches
178              * the events on its EventQueue, this event will be sent to
179              * the speechAWTEventTarget for processing.
180              */
181             if (waitUntilDispatched) {
182                 Object lock = new Object();
183                 synchronized(lock) {
184                     systemEventQueue.postEvent(
185                         new SpeechAWTEvent(speechAWTEventTarget,
186                                            dispatcher,
187                                            event,
188                                            lock));
189                     try {
190                         lock.wait();
191                     } catch (InterruptedException e) {
192                     }
193                 }
194             } else {
195                 systemEventQueue.postEvent(
196                     new SpeechAWTEvent(speechAWTEventTarget,
197                                        dispatcher,
198                                        event));
199             }
200         } else {
201             dispatcher.dispatchSpeechEvent(event);
202         }
203     }
204
205     /**
206      * Inner class used to handle events as they are dispatched from the
207      * AWT event queue.
208      *
209      * @see #postSpeechEvent
210      */
211     protected static class SpeechAWTEventTarget extends Component {
212         SpeechAWTEventTarget() {
213             super();
214             enableEvents(SpeechAWTEvent.EVENT_ID);
215         }
216         protected void processEvent(AWTEvent event) {
217             if (event instanceof SpeechAWTEvent) {
218                 SpeechAWTEvent sae = (SpeechAWTEvent) event;
219                 sae.dispatcher.dispatchSpeechEvent(sae.event);
220                 if (sae.lock != null) {
221                     synchronized(sae.lock) {
222                         sae.lock.notify();
223                     }
224                 }
225             }
226         }
227     }
228
229     /**
230      * Inner class that defines SpeechAWTEvents.  These are created and
231      * posted to the AWT EventQueue by the postSpeechEvent method.
232      *
233      * @see #postSpeechEvent
234      */
235     protected static class SpeechAWTEvent extends AWTEvent {
236         static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 14830;
237         SpeechEventDispatcher dispatcher = null;
238         SpeechEvent event = null;
239         Object lock = null;
240         SpeechAWTEvent(SpeechAWTEventTarget  target,
241                        SpeechEventDispatcher dispatcher,
242                        SpeechEvent           event) {
243             this(target,dispatcher,event,null);
244         }
245         SpeechAWTEvent(SpeechAWTEventTarget  target,
246                        SpeechEventDispatcher dispatcher,
247                        SpeechEvent           event,
248                        Object                lock) {
249             super(target, EVENT_ID);
250             this.dispatcher = dispatcher;
251             this.event = event;
252             this.lock = lock;
253         }
254     }
255 }
256
257         
258
259
260