/*
 * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.awt.event;
import java.awt.ActiveEvent;
import java.awt.AWTEvent;
/**
 * An event which executes the run() method on a Runnable
 *  when dispatched by the AWT event dispatcher thread. This class can
 * be used as a reference implementation of ActiveEvent rather
 * than declaring a new class and defining dispatch().
 *
 * Instances of this class are placed on the EventQueue by calls
 * to invokeLater and invokeAndWait. Client code
 * can use this fact to write replacement functions for invokeLater
 *  and invokeAndWait without writing special-case code
 * in any 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 {
    /**
     * 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.
     *
     * @see #isDispatched
     */
    protected Object notifier;
    /**
     * Indicates whether the run() method of the 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 InvocationEvent with the specified
     * source which will execute the runnable's run
     * method when dispatched.
     * 
This is a convenience constructor. An invocation of the form * 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 IllegalArgumentException
     * if source is null.
     *
     * @param source    The Object that originated the event
     * @param runnable  The Runnable whose run
     *                  method will be executed
     * @throws IllegalArgumentException if source is null
     *
     * @see #getSource()
     * @see #InvocationEvent(Object, Runnable, Object, boolean)
     */
    public InvocationEvent(Object source, Runnable runnable) {
        this(source, runnable, null, false);
    }
    /**
     * Constructs an InvocationEvent with the specified
     * source which will execute the runnable's run
     * method when dispatched.  If notifier is non-null,
     * notifyAll() will be called on it
     * immediately after run has returned or thrown an exception.
     * 
An invocation of the form 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 IllegalArgumentException
     * if source is null.
     *
     * @param source            The Object that originated
     *                          the event
     * @param runnable          The Runnable whose
     *                          run method will be
     *                          executed
     * @param notifier          The {@code Object} whose notifyAll
     *                          method will be called after
     *                          Runnable.run has returned or
     *                          thrown an exception
     * @param catchThrowables   Specifies whether dispatch
     *                          should catch Throwable when executing
     *                          the Runnable's run
     *                          method, or should instead propagate those
     *                          Throwables to the EventDispatchThread's
     *                          dispatch loop
     * @throws IllegalArgumentException if 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, catchThrowables);
    }
    /**
     * Constructs an InvocationEvent with the specified
     * source and ID which will execute the runnable's run
     * method when dispatched.  If notifier is non-null,
     * notifyAll will be called on it immediately after
     * run has returned or thrown an exception.
     * 
This method throws an
     * IllegalArgumentException if source
     * is null.
     *
     * @param source            The 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 Runnable whose
     *                          run method will be executed
     * @param notifier          The Object whose notifyAll
     *                          method will be called after
     *                          Runnable.run has returned or
     *                          thrown an exception
     * @param catchThrowables   Specifies whether dispatch
     *                          should catch Throwable when executing the
     *                          Runnable's run
     *                          method, or should instead propagate those
     *                          Throwables to the EventDispatchThread's
     *                          dispatch loop
     * @throws IllegalArgumentException if source is null
     * @see #getSource()
     * @see #getID()
     */
    protected InvocationEvent(Object source, int id, Runnable runnable,
                              Object notifier, boolean catchThrowables) {
        super(source, id);
        this.runnable = runnable;
        this.notifier = notifier;
        this.catchExceptions = catchThrowables;
        this.when = System.currentTimeMillis();
    }
    /**
     * Executes the Runnable's run() method and notifies the
     * notifier (if any) when 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 {
            dispatched = true;
            if (notifier != null) {
                synchronized (notifier) {
                    notifier.notifyAll();
                }
            }
        }
    }
    /**
     * Returns any Exception caught while executing the Runnable's 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 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; } /** * 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; } }