1/*
2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt.event;
27
28import sun.awt.AWTAccessor;
29
30import java.awt.ActiveEvent;
31import java.awt.AWTEvent;
32
33/**
34 * An event which executes the {@code run()} method on a {@code Runnable
35 * } when dispatched by the AWT event dispatcher thread. This class can
36 * be used as a reference implementation of {@code ActiveEvent} rather
37 * than declaring a new class and defining {@code dispatch()}.<p>
38 *
39 * Instances of this class are placed on the {@code EventQueue} by calls
40 * to {@code invokeLater} and {@code invokeAndWait}. Client code
41 * can use this fact to write replacement functions for {@code invokeLater
42 * } and {@code invokeAndWait} without writing special-case code
43 * in any {@code AWTEventListener} objects.
44 * <p>
45 * An unspecified behavior will be caused if the {@code id} parameter
46 * of any particular {@code InvocationEvent} instance is not
47 * in the range from {@code INVOCATION_FIRST} to {@code INVOCATION_LAST}.
48 *
49 * @author      Fred Ecks
50 * @author      David Mendenhall
51 *
52 * @see         java.awt.ActiveEvent
53 * @see         java.awt.EventQueue#invokeLater
54 * @see         java.awt.EventQueue#invokeAndWait
55 * @see         AWTEventListener
56 *
57 * @since       1.2
58 */
59public class InvocationEvent extends AWTEvent implements ActiveEvent {
60
61    static {
62        AWTAccessor.setInvocationEventAccessor(new AWTAccessor.InvocationEventAccessor() {
63            @Override
64            public void dispose(InvocationEvent invocationEvent) {
65                invocationEvent.finishedDispatching(false);
66            }
67        });
68    }
69
70    /**
71     * Marks the first integer id for the range of invocation event ids.
72     */
73    public static final int INVOCATION_FIRST = 1200;
74
75    /**
76     * The default id for all InvocationEvents.
77     */
78    public static final int INVOCATION_DEFAULT = INVOCATION_FIRST;
79
80    /**
81     * Marks the last integer id for the range of invocation event ids.
82     */
83    public static final int INVOCATION_LAST = INVOCATION_DEFAULT;
84
85    /**
86     * The Runnable whose run() method will be called.
87     */
88    protected Runnable runnable;
89
90    /**
91     * The (potentially null) Object whose notifyAll() method will be called
92     * immediately after the Runnable.run() method has returned or thrown an exception
93     * or after the event was disposed.
94     *
95     * @see #isDispatched
96     */
97    protected volatile Object notifier;
98
99    /**
100     * The (potentially null) Runnable whose run() method will be called
101     * immediately after the event was dispatched or disposed.
102     *
103     * @see #isDispatched
104     * @since 1.8
105     */
106    private final Runnable listener;
107
108    /**
109     * Indicates whether the {@code run()} method of the {@code runnable}
110     * was executed or not.
111     *
112     * @see #isDispatched
113     * @since 1.7
114     */
115    private volatile boolean dispatched = false;
116
117    /**
118     * Set to true if dispatch() catches Throwable and stores it in the
119     * exception instance variable. If false, Throwables are propagated up
120     * to the EventDispatchThread's dispatch loop.
121     */
122    protected boolean catchExceptions;
123
124    /**
125     * The (potentially null) Exception thrown during execution of the
126     * Runnable.run() method. This variable will also be null if a particular
127     * instance does not catch exceptions.
128     */
129    private Exception exception = null;
130
131    /**
132     * The (potentially null) Throwable thrown during execution of the
133     * Runnable.run() method. This variable will also be null if a particular
134     * instance does not catch exceptions.
135     */
136    private Throwable throwable = null;
137
138    /**
139     * The timestamp of when this event occurred.
140     *
141     * @serial
142     * @see #getWhen
143     */
144    private long when;
145
146    /*
147     * JDK 1.1 serialVersionUID.
148     */
149    private static final long serialVersionUID = 436056344909459450L;
150
151    /**
152     * Constructs an {@code InvocationEvent} with the specified
153     * source which will execute the runnable's {@code run}
154     * method when dispatched.
155     * <p>This is a convenience constructor.  An invocation of the form
156     * {@code InvocationEvent(source, runnable)}
157     * behaves in exactly the same way as the invocation of
158     * {@link #InvocationEvent(Object, Runnable, Object, boolean)
159     * InvocationEvent(source, runnable, null, false)}.
160     * <p> This method throws an {@code IllegalArgumentException}
161     * if {@code source} is {@code null}.
162     *
163     * @param source    The {@code Object} that originated the event
164     * @param runnable  The {@code Runnable} whose {@code run}
165     *                  method will be executed
166     * @throws IllegalArgumentException if {@code source} is null
167     *
168     * @see #getSource()
169     * @see #InvocationEvent(Object, Runnable, Object, boolean)
170     */
171    public InvocationEvent(Object source, Runnable runnable) {
172        this(source, INVOCATION_DEFAULT, runnable, null, null, false);
173    }
174
175    /**
176     * Constructs an {@code InvocationEvent} with the specified
177     * source which will execute the runnable's {@code run}
178     * method when dispatched.  If notifier is non-{@code null},
179     * {@code notifyAll()} will be called on it
180     * immediately after {@code run} has returned or thrown an exception.
181     * <p>An invocation of the form
182     * {@code InvocationEvent(source, runnable, notifier, catchThrowables)}
183     * behaves in exactly the same way as the invocation of
184     * {@link #InvocationEvent(Object, int, Runnable, Object, boolean)
185     * InvocationEvent(source, InvocationEvent.INVOCATION_DEFAULT, runnable, notifier, catchThrowables)}.
186     * <p>This method throws an {@code IllegalArgumentException}
187     * if {@code source} is {@code null}.
188     *
189     * @param source            The {@code Object} that originated
190     *                          the event
191     * @param runnable          The {@code Runnable} whose
192     *                          {@code run} method will be
193     *                          executed
194     * @param notifier          The {@code Object} whose {@code notifyAll}
195     *                          method will be called after
196     *                          {@code Runnable.run} has returned or
197     *                          thrown an exception or after the event was
198     *                          disposed
199     * @param catchThrowables   Specifies whether {@code dispatch}
200     *                          should catch Throwable when executing
201     *                          the {@code Runnable}'s {@code run}
202     *                          method, or should instead propagate those
203     *                          Throwables to the EventDispatchThread's
204     *                          dispatch loop
205     * @throws IllegalArgumentException if {@code source} is null
206     *
207     * @see #getSource()
208     * @see     #InvocationEvent(Object, int, Runnable, Object, boolean)
209     */
210    public InvocationEvent(Object source, Runnable runnable, Object notifier,
211                           boolean catchThrowables) {
212        this(source, INVOCATION_DEFAULT, runnable, notifier, null, catchThrowables);
213    }
214
215    /**
216     * Constructs an {@code InvocationEvent} with the specified
217     * source which will execute the runnable's {@code run}
218     * method when dispatched.  If listener is non-{@code null},
219     * {@code listener.run()} will be called immediately after
220     * {@code run} has returned, thrown an exception or the event
221     * was disposed.
222     * <p>This method throws an {@code IllegalArgumentException}
223     * if {@code source} is {@code null}.
224     *
225     * @param source            The {@code Object} that originated
226     *                          the event
227     * @param runnable          The {@code Runnable} whose
228     *                          {@code run} method will be
229     *                          executed
230     * @param listener          The {@code Runnable}Runnable whose
231     *                          {@code run()} method will be called
232     *                          after the {@code InvocationEvent}
233     *                          was dispatched or disposed
234     * @param catchThrowables   Specifies whether {@code dispatch}
235     *                          should catch Throwable when executing
236     *                          the {@code Runnable}'s {@code run}
237     *                          method, or should instead propagate those
238     *                          Throwables to the EventDispatchThread's
239     *                          dispatch loop
240     * @throws IllegalArgumentException if {@code source} is null
241     */
242    public InvocationEvent(Object source, Runnable runnable, Runnable listener,
243                           boolean catchThrowables)  {
244        this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables);
245    }
246
247    /**
248     * Constructs an {@code InvocationEvent} with the specified
249     * source and ID which will execute the runnable's {@code run}
250     * method when dispatched.  If notifier is non-{@code null},
251     * {@code notifyAll} will be called on it immediately after
252     * {@code run} has returned or thrown an exception.
253     * <p>This method throws an
254     * {@code IllegalArgumentException} if {@code source}
255     * is {@code null}.
256     *
257     * @param source            The {@code Object} that originated
258     *                          the event
259     * @param id     An integer indicating the type of event.
260     *                     For information on allowable values, see
261     *                     the class description for {@link InvocationEvent}
262     * @param runnable          The {@code Runnable} whose
263     *                          {@code run} method will be executed
264     * @param notifier          The {@code Object} whose {@code notifyAll}
265     *                          method will be called after
266     *                          {@code Runnable.run} has returned or
267     *                          thrown an exception or after the event was
268     *                          disposed
269     * @param catchThrowables   Specifies whether {@code dispatch}
270     *                          should catch Throwable when executing the
271     *                          {@code Runnable}'s {@code run}
272     *                          method, or should instead propagate those
273     *                          Throwables to the EventDispatchThread's
274     *                          dispatch loop
275     * @throws IllegalArgumentException if {@code source} is null
276     * @see #getSource()
277     * @see #getID()
278     */
279    protected InvocationEvent(Object source, int id, Runnable runnable,
280                              Object notifier, boolean catchThrowables) {
281        this(source, id, runnable, notifier, null, catchThrowables);
282    }
283
284    private InvocationEvent(Object source, int id, Runnable runnable,
285                            Object notifier, Runnable listener, boolean catchThrowables) {
286        super(source, id);
287        this.runnable = runnable;
288        this.notifier = notifier;
289        this.listener = listener;
290        this.catchExceptions = catchThrowables;
291        this.when = System.currentTimeMillis();
292    }
293    /**
294     * Executes the Runnable's {@code run()} method and notifies the
295     * notifier (if any) when {@code run()} has returned or thrown an exception.
296     *
297     * @see #isDispatched
298     */
299    public void dispatch() {
300        try {
301            if (catchExceptions) {
302                try {
303                    runnable.run();
304                }
305                catch (Throwable t) {
306                    if (t instanceof Exception) {
307                        exception = (Exception) t;
308                    }
309                    throwable = t;
310                }
311            }
312            else {
313                runnable.run();
314            }
315        } finally {
316            finishedDispatching(true);
317        }
318    }
319
320    /**
321     * Returns any Exception caught while executing
322     * the Runnable's {@code run()} method.
323     *
324     * @return  A reference to the Exception if one was thrown; null if no
325     *          Exception was thrown or if this InvocationEvent does not
326     *          catch exceptions
327     */
328    public Exception getException() {
329        return (catchExceptions) ? exception : null;
330    }
331
332    /**
333     * Returns any Throwable caught while executing
334     * the Runnable's {@code run()} method.
335     *
336     * @return  A reference to the Throwable if one was thrown; null if no
337     *          Throwable was thrown or if this InvocationEvent does not
338     *          catch Throwables
339     * @since 1.5
340     */
341    public Throwable getThrowable() {
342        return (catchExceptions) ? throwable : null;
343    }
344
345    /**
346     * Returns the timestamp of when this event occurred.
347     *
348     * @return this event's timestamp
349     * @since 1.4
350     */
351    public long getWhen() {
352        return when;
353    }
354
355    /**
356     * Returns {@code true} if the event is dispatched or any exception is
357     * thrown while dispatching, {@code false} otherwise. The method should
358     * be called by a waiting thread that calls the {@code notifier.wait()} method.
359     * Since spurious wakeups are possible (as explained in {@link Object#wait()}),
360     * this method should be used in a waiting loop to ensure that the event
361     * got dispatched:
362     * <pre>
363     *     while (!event.isDispatched()) {
364     *         notifier.wait();
365     *     }
366     * </pre>
367     * If the waiting thread wakes up without dispatching the event,
368     * the {@code isDispatched()} method returns {@code false}, and
369     * the {@code while} loop executes once more, thus, causing
370     * the awakened thread to revert to the waiting mode.
371     * <p>
372     * If the {@code notifier.notifyAll()} happens before the waiting thread
373     * enters the {@code notifier.wait()} method, the {@code while} loop ensures
374     * that the waiting thread will not enter the {@code notifier.wait()} method.
375     * Otherwise, there is no guarantee that the waiting thread will ever be woken
376     * from the wait.
377     *
378     * @return {@code true} if the event has been dispatched, or any exception
379     * has been thrown while dispatching, {@code false} otherwise
380     * @see #dispatch
381     * @see #notifier
382     * @see #catchExceptions
383     * @since 1.7
384     */
385    public boolean isDispatched() {
386        return dispatched;
387    }
388
389    /**
390     * Called when the event was dispatched or disposed
391     * @param dispatched true if the event was dispatched
392     *                   false if the event was disposed
393     */
394    private void finishedDispatching(boolean dispatched) {
395        this.dispatched = dispatched;
396
397        if (notifier != null) {
398            synchronized (notifier) {
399                notifier.notifyAll();
400            }
401        }
402
403        if (listener != null) {
404            listener.run();
405        }
406    }
407
408    /**
409     * Returns a parameter string identifying this event.
410     * This method is useful for event-logging and for debugging.
411     *
412     * @return  A string identifying the event and its attributes
413     */
414    public String paramString() {
415        String typeStr;
416        switch(id) {
417            case INVOCATION_DEFAULT:
418                typeStr = "INVOCATION_DEFAULT";
419                break;
420            default:
421                typeStr = "unknown type";
422        }
423        return typeStr + ",runnable=" + runnable + ",notifier=" + notifier +
424            ",catchExceptions=" + catchExceptions + ",when=" + when;
425    }
426}
427