/* * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.awt.event; import sun.awt.AWTAccessor; import java.awt.ActiveEvent; import java.awt.AWTEvent; /** * An event which executes the {@code run()} method on a {@code Runnable * } when dispatched by the AWT event dispatcher thread. This class can * be used as a reference implementation of {@code ActiveEvent} rather * than declaring a new class and defining {@code dispatch()}.

* * Instances of this class are placed on the {@code EventQueue} by calls * to {@code invokeLater} and {@code invokeAndWait}. Client code * can use this fact to write replacement functions for {@code invokeLater * } and {@code invokeAndWait} without writing special-case code * in any {@code AWTEventListener} objects. *

* An unspecified behavior will be caused if the {@code id} parameter * of any particular {@code InvocationEvent} instance is not * in the range from {@code INVOCATION_FIRST} to {@code INVOCATION_LAST}. * * @author Fred Ecks * @author David Mendenhall * * @see java.awt.ActiveEvent * @see java.awt.EventQueue#invokeLater * @see java.awt.EventQueue#invokeAndWait * @see AWTEventListener * * @since 1.2 */ public class InvocationEvent extends AWTEvent implements ActiveEvent { static { AWTAccessor.setInvocationEventAccessor(new AWTAccessor.InvocationEventAccessor() { @Override public void dispose(InvocationEvent invocationEvent) { invocationEvent.finishedDispatching(false); } }); } /** * Marks the first integer id for the range of invocation event ids. */ public static final int INVOCATION_FIRST = 1200; /** * The default id for all InvocationEvents. */ public static final int INVOCATION_DEFAULT = INVOCATION_FIRST; /** * Marks the last integer id for the range of invocation event ids. */ public static final int INVOCATION_LAST = INVOCATION_DEFAULT; /** * The Runnable whose run() method will be called. */ protected Runnable runnable; /** * The (potentially null) Object whose notifyAll() method will be called * immediately after the Runnable.run() method has returned or thrown an exception * or after the event was disposed. * * @see #isDispatched */ protected volatile Object notifier; /** * The (potentially null) Runnable whose run() method will be called * immediately after the event was dispatched or disposed. * * @see #isDispatched * @since 1.8 */ private final Runnable listener; /** * Indicates whether the {@code run()} method of the {@code runnable} * was executed or not. * * @see #isDispatched * @since 1.7 */ private volatile boolean dispatched = false; /** * Set to true if dispatch() catches Throwable and stores it in the * exception instance variable. If false, Throwables are propagated up * to the EventDispatchThread's dispatch loop. */ protected boolean catchExceptions; /** * The (potentially null) Exception thrown during execution of the * Runnable.run() method. This variable will also be null if a particular * instance does not catch exceptions. */ private Exception exception = null; /** * The (potentially null) Throwable thrown during execution of the * Runnable.run() method. This variable will also be null if a particular * instance does not catch exceptions. */ private Throwable throwable = null; /** * The timestamp of when this event occurred. * * @serial * @see #getWhen */ private long when; /* * JDK 1.1 serialVersionUID. */ private static final long serialVersionUID = 436056344909459450L; /** * Constructs an {@code InvocationEvent} with the specified * source which will execute the runnable's {@code run} * method when dispatched. *

This is a convenience constructor. An invocation of the form * {@code InvocationEvent(source, runnable)} * behaves in exactly the same way as the invocation of * {@link #InvocationEvent(Object, Runnable, Object, boolean) * InvocationEvent(source, runnable, null, false)}. *

This method throws an {@code IllegalArgumentException} * if {@code source} is {@code null}. * * @param source The {@code Object} that originated the event * @param runnable The {@code Runnable} whose {@code run} * method will be executed * @throws IllegalArgumentException if {@code source} is null * * @see #getSource() * @see #InvocationEvent(Object, Runnable, Object, boolean) */ public InvocationEvent(Object source, Runnable runnable) { this(source, INVOCATION_DEFAULT, runnable, null, null, false); } /** * Constructs an {@code InvocationEvent} with the specified * source which will execute the runnable's {@code run} * method when dispatched. If notifier is non-{@code null}, * {@code notifyAll()} will be called on it * immediately after {@code run} has returned or thrown an exception. *

An invocation of the form * {@code InvocationEvent(source, runnable, notifier, catchThrowables)} * behaves in exactly the same way as the invocation of * {@link #InvocationEvent(Object, int, Runnable, Object, boolean) * InvocationEvent(source, InvocationEvent.INVOCATION_DEFAULT, runnable, notifier, catchThrowables)}. *

This method throws an {@code IllegalArgumentException} * if {@code source} is {@code null}. * * @param source The {@code Object} that originated * the event * @param runnable The {@code Runnable} whose * {@code run} method will be * executed * @param notifier The {@code Object} whose {@code notifyAll} * method will be called after * {@code Runnable.run} has returned or * thrown an exception or after the event was * disposed * @param catchThrowables Specifies whether {@code dispatch} * should catch Throwable when executing * the {@code Runnable}'s {@code run} * method, or should instead propagate those * Throwables to the EventDispatchThread's * dispatch loop * @throws IllegalArgumentException if {@code source} is null * * @see #getSource() * @see #InvocationEvent(Object, int, Runnable, Object, boolean) */ public InvocationEvent(Object source, Runnable runnable, Object notifier, boolean catchThrowables) { this(source, INVOCATION_DEFAULT, runnable, notifier, null, catchThrowables); } /** * Constructs an {@code InvocationEvent} with the specified * source which will execute the runnable's {@code run} * method when dispatched. If listener is non-{@code null}, * {@code listener.run()} will be called immediately after * {@code run} has returned, thrown an exception or the event * was disposed. *

This method throws an {@code IllegalArgumentException} * if {@code source} is {@code null}. * * @param source The {@code Object} that originated * the event * @param runnable The {@code Runnable} whose * {@code run} method will be * executed * @param listener The {@code Runnable}Runnable whose * {@code run()} method will be called * after the {@code InvocationEvent} * was dispatched or disposed * @param catchThrowables Specifies whether {@code dispatch} * should catch Throwable when executing * the {@code Runnable}'s {@code run} * method, or should instead propagate those * Throwables to the EventDispatchThread's * dispatch loop * @throws IllegalArgumentException if {@code source} is null */ public InvocationEvent(Object source, Runnable runnable, Runnable listener, boolean catchThrowables) { this(source, INVOCATION_DEFAULT, runnable, null, listener, catchThrowables); } /** * Constructs an {@code InvocationEvent} with the specified * source and ID which will execute the runnable's {@code run} * method when dispatched. If notifier is non-{@code null}, * {@code notifyAll} will be called on it immediately after * {@code run} has returned or thrown an exception. *

This method throws an * {@code IllegalArgumentException} if {@code source} * is {@code null}. * * @param source The {@code Object} that originated * the event * @param id An integer indicating the type of event. * For information on allowable values, see * the class description for {@link InvocationEvent} * @param runnable The {@code Runnable} whose * {@code run} method will be executed * @param notifier The {@code Object} whose {@code notifyAll} * method will be called after * {@code Runnable.run} has returned or * thrown an exception or after the event was * disposed * @param catchThrowables Specifies whether {@code dispatch} * should catch Throwable when executing the * {@code Runnable}'s {@code run} * method, or should instead propagate those * Throwables to the EventDispatchThread's * dispatch loop * @throws IllegalArgumentException if {@code source} is null * @see #getSource() * @see #getID() */ protected InvocationEvent(Object source, int id, Runnable runnable, Object notifier, boolean catchThrowables) { this(source, id, runnable, notifier, null, catchThrowables); } private InvocationEvent(Object source, int id, Runnable runnable, Object notifier, Runnable listener, boolean catchThrowables) { super(source, id); this.runnable = runnable; this.notifier = notifier; this.listener = listener; this.catchExceptions = catchThrowables; this.when = System.currentTimeMillis(); } /** * Executes the Runnable's {@code run()} method and notifies the * notifier (if any) when {@code run()} has returned or thrown an exception. * * @see #isDispatched */ public void dispatch() { try { if (catchExceptions) { try { runnable.run(); } catch (Throwable t) { if (t instanceof Exception) { exception = (Exception) t; } throwable = t; } } else { runnable.run(); } } finally { finishedDispatching(true); } } /** * Returns any Exception caught while executing * the Runnable's {@code run()} method. * * @return A reference to the Exception if one was thrown; null if no * Exception was thrown or if this InvocationEvent does not * catch exceptions */ public Exception getException() { return (catchExceptions) ? exception : null; } /** * Returns any Throwable caught while executing * the Runnable's {@code run()} method. * * @return A reference to the Throwable if one was thrown; null if no * Throwable was thrown or if this InvocationEvent does not * catch Throwables * @since 1.5 */ public Throwable getThrowable() { return (catchExceptions) ? throwable : null; } /** * Returns the timestamp of when this event occurred. * * @return this event's timestamp * @since 1.4 */ public long getWhen() { return when; } /** * Returns {@code true} if the event is dispatched or any exception is * thrown while dispatching, {@code false} otherwise. The method should * be called by a waiting thread that calls the {@code notifier.wait()} method. * Since spurious wakeups are possible (as explained in {@link Object#wait()}), * this method should be used in a waiting loop to ensure that the event * got dispatched: *

     *     while (!event.isDispatched()) {
     *         notifier.wait();
     *     }
     * 
* If the waiting thread wakes up without dispatching the event, * the {@code isDispatched()} method returns {@code false}, and * the {@code while} loop executes once more, thus, causing * the awakened thread to revert to the waiting mode. *

* If the {@code notifier.notifyAll()} happens before the waiting thread * enters the {@code notifier.wait()} method, the {@code while} loop ensures * that the waiting thread will not enter the {@code notifier.wait()} method. * Otherwise, there is no guarantee that the waiting thread will ever be woken * from the wait. * * @return {@code true} if the event has been dispatched, or any exception * has been thrown while dispatching, {@code false} otherwise * @see #dispatch * @see #notifier * @see #catchExceptions * @since 1.7 */ public boolean isDispatched() { return dispatched; } /** * Called when the event was dispatched or disposed * @param dispatched true if the event was dispatched * false if the event was disposed */ private void finishedDispatching(boolean dispatched) { this.dispatched = dispatched; if (notifier != null) { synchronized (notifier) { notifier.notifyAll(); } } if (listener != null) { listener.run(); } } /** * Returns a parameter string identifying this event. * This method is useful for event-logging and for debugging. * * @return A string identifying the event and its attributes */ public String paramString() { String typeStr; switch(id) { case INVOCATION_DEFAULT: typeStr = "INVOCATION_DEFAULT"; break; default: typeStr = "unknown type"; } return typeStr + ",runnable=" + runnable + ",notifier=" + notifier + ",catchExceptions=" + catchExceptions + ",when=" + when; } }