/* | 
|
 * Copyright (c) 2005, 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 javax.swing;  | 
|
import java.lang.ref.WeakReference;  | 
|
import java.security.AccessController;  | 
|
import java.security.PrivilegedAction;  | 
|
import java.beans.PropertyChangeListener;  | 
|
import java.beans.PropertyChangeSupport;  | 
|
import java.beans.PropertyChangeEvent;  | 
|
import java.util.List;  | 
|
import java.util.concurrent.*;  | 
|
import java.util.concurrent.locks.*;  | 
|
import java.awt.event.*;  | 
|
import javax.swing.SwingUtilities;  | 
|
import sun.awt.AppContext;  | 
|
import sun.swing.AccumulativeRunnable;  | 
|
/** | 
|
 * An abstract class to perform lengthy GUI-interaction tasks in a | 
|
 * background thread. Several background threads can be used to execute such | 
|
 * tasks. However, the exact strategy of choosing a thread for any particular | 
|
 * {@code SwingWorker} is unspecified and should not be relied on. | 
|
 * <p> | 
|
 * When writing a multi-threaded application using Swing, there are | 
|
 * two constraints to keep in mind: | 
|
 * (refer to | 
|
 * <a href="https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html"> | 
|
 *   Concurrency in Swing | 
|
 * </a> for more details): | 
|
 * <ul> | 
|
 *   <li> Time-consuming tasks should not be run on the <i>Event | 
|
 *        Dispatch Thread</i>. Otherwise the application becomes unresponsive. | 
|
 *   </li> | 
|
 *   <li> Swing components should be accessed  on the <i>Event | 
|
 *        Dispatch Thread</i> only. | 
|
 *   </li> | 
|
 * </ul> | 
|
 * | 
|
 * | 
|
 * <p> | 
|
 * These constraints mean that a GUI application with time intensive | 
|
 * computing needs at least two threads:  1) a thread to perform the lengthy | 
|
 * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related | 
|
 * activities.  This involves inter-thread communication which can be | 
|
 * tricky to implement. | 
|
 * | 
|
 * <p> | 
|
 * {@code SwingWorker} is designed for situations where you need to have a long | 
|
 * running task run in a background thread and provide updates to the UI | 
|
 * either when done, or while processing. | 
|
 * Subclasses of {@code SwingWorker} must implement | 
|
 * the {@link #doInBackground} method to perform the background computation. | 
|
 * | 
|
 * | 
|
 * <p> | 
|
 * <b>Workflow</b> | 
|
 * <p> | 
|
 * There are three threads involved in the life cycle of a | 
|
 * {@code SwingWorker} : | 
|
 * <ul> | 
|
 * <li> | 
|
 * <p> | 
|
 * <i>Current</i> thread: The {@link #execute} method is | 
|
 * called on this thread. It schedules {@code SwingWorker} for the execution on a | 
|
 * <i>worker</i> | 
|
 * thread and returns immediately. One can wait for the {@code SwingWorker} to | 
|
 * complete using the {@link #get get} methods. | 
|
 * <li> | 
|
 * <p> | 
|
 * <i>Worker</i> thread: The {@link #doInBackground} | 
|
 * method is called on this thread. | 
|
 * This is where all background activities should happen. To notify | 
|
 * {@code PropertyChangeListeners} about bound properties changes use the | 
|
 * {@link #firePropertyChange firePropertyChange} and | 
|
 * {@link #getPropertyChangeSupport} methods. By default there are two bound | 
|
 * properties available: {@code state} and {@code progress}. | 
|
 * <li> | 
|
 * <p> | 
|
 * <i>Event Dispatch Thread</i>:  All Swing related activities occur | 
|
 * on this thread. {@code SwingWorker} invokes the | 
|
 * {@link #process process} and {@link #done} methods and notifies | 
|
 * any {@code PropertyChangeListeners} on this thread. | 
|
 * </ul> | 
|
 * | 
|
 * <p> | 
|
 * Often, the <i>Current</i> thread is the <i>Event Dispatch | 
|
 * Thread</i>. | 
|
 * | 
|
 * | 
|
 * <p> | 
|
 * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread, | 
|
 * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the | 
|
 * {@code state} property change to {@code StateValue.STARTED}.  After the | 
|
 * {@code doInBackground} method is finished the {@code done} method is | 
|
 * executed.  Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} | 
|
 * about the {@code state} property change to {@code StateValue.DONE}. | 
|
 * | 
|
 * <p> | 
|
 * {@code SwingWorker} is only designed to be executed once.  Executing a | 
|
 * {@code SwingWorker} more than once will not result in invoking the | 
|
 * {@code doInBackground} method twice. | 
|
 * | 
|
 * <p> | 
|
 * <b>Sample Usage</b> | 
|
 * <p> | 
|
 * The following example illustrates the simplest use case.  Some | 
|
 * processing is done in the background and when done you update a Swing | 
|
 * component. | 
|
 * | 
|
 * <p> | 
|
 * Say we want to find the "Meaning of Life" and display the result in | 
|
 * a {@code JLabel}. | 
|
 * | 
|
 * <pre> | 
|
 *   final JLabel label; | 
|
 *   class MeaningOfLifeFinder extends SwingWorker<String, Object> { | 
|
 *       {@code @Override} | 
|
 *       public String doInBackground() { | 
|
 *           return findTheMeaningOfLife(); | 
|
 *       } | 
|
 * | 
|
 *       {@code @Override} | 
|
 *       protected void done() { | 
|
 *           try { | 
|
 *               label.setText(get()); | 
|
 *           } catch (Exception ignore) { | 
|
 *           } | 
|
 *       } | 
|
 *   } | 
|
 * | 
|
 *   (new MeaningOfLifeFinder()).execute(); | 
|
 * </pre> | 
|
 * | 
|
 * <p> | 
|
 * The next example is useful in situations where you wish to process data | 
|
 * as it is ready on the <i>Event Dispatch Thread</i>. | 
|
 * | 
|
 * <p> | 
|
 * Now we want to find the first N prime numbers and display the results in a | 
|
 * {@code JTextArea}.  While this is computing, we want to update our | 
|
 * progress in a {@code JProgressBar}.  Finally, we also want to print | 
|
 * the prime numbers to {@code System.out}. | 
|
 * <pre> | 
|
 * class PrimeNumbersTask extends | 
|
 *         SwingWorker<List<Integer>, Integer> { | 
|
 *     PrimeNumbersTask(JTextArea textArea, int numbersToFind) { | 
|
 *         //initialize | 
|
 *     } | 
|
 * | 
|
 *     {@code @Override} | 
|
 *     public List<Integer> doInBackground() { | 
|
 *         while (! enough && ! isCancelled()) { | 
|
 *                 number = nextPrimeNumber(); | 
|
 *                 publish(number); | 
|
 *                 setProgress(100 * numbers.size() / numbersToFind); | 
|
 *             } | 
|
 *         } | 
|
 *         return numbers; | 
|
 *     } | 
|
 * | 
|
 *     {@code @Override} | 
|
 *     protected void process(List<Integer> chunks) { | 
|
 *         for (int number : chunks) { | 
|
 *             textArea.append(number + "\n"); | 
|
 *         } | 
|
 *     } | 
|
 * } | 
|
 * | 
|
 * JTextArea textArea = new JTextArea(); | 
|
 * final JProgressBar progressBar = new JProgressBar(0, 100); | 
|
 * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); | 
|
 * task.addPropertyChangeListener( | 
|
 *     new PropertyChangeListener() { | 
|
 *         public  void propertyChange(PropertyChangeEvent evt) { | 
|
 *             if ("progress".equals(evt.getPropertyName())) { | 
|
 *                 progressBar.setValue((Integer)evt.getNewValue()); | 
|
 *             } | 
|
 *         } | 
|
 *     }); | 
|
 * | 
|
 * task.execute(); | 
|
 * System.out.println(task.get()); //prints all prime numbers we have got | 
|
 * </pre> | 
|
 * | 
|
 * <p> | 
|
 * Because {@code SwingWorker} implements {@code Runnable}, a | 
|
 * {@code SwingWorker} can be submitted to an | 
|
 * {@link java.util.concurrent.Executor} for execution. | 
|
 * | 
|
 * @author Igor Kushnirskiy | 
|
 * | 
|
 * @param <T> the result type returned by this {@code SwingWorker's} | 
|
 *        {@code doInBackground} and {@code get} methods | 
|
 * @param <V> the type used for carrying out intermediate results by this | 
|
 *        {@code SwingWorker's} {@code publish} and {@code process} methods | 
|
 * | 
|
 * @since 1.6 | 
|
*/  | 
|
public abstract class SwingWorker<T, V> implements RunnableFuture<T> {  | 
|
    /** | 
|
     * number of worker threads. | 
|
*/  | 
|
private static final int MAX_WORKER_THREADS = 10;  | 
|
    /** | 
|
     * current progress. | 
|
*/  | 
|
private volatile int progress;  | 
|
    /** | 
|
     * current state. | 
|
*/  | 
|
private volatile StateValue state;  | 
|
    /** | 
|
     * everything is run inside this FutureTask. Also it is used as | 
|
     * a delegatee for the Future API. | 
|
*/  | 
|
private final FutureTask<T> future;  | 
|
    /** | 
|
     * all propertyChangeSupport goes through this. | 
|
*/  | 
|
private final PropertyChangeSupport propertyChangeSupport;  | 
|
    /** | 
|
     * handler for {@code process} mehtod. | 
|
*/  | 
|
private AccumulativeRunnable<V> doProcess;  | 
|
    /** | 
|
     * handler for progress property change notifications. | 
|
*/  | 
|
private AccumulativeRunnable<Integer> doNotifyProgressChange;  | 
|
private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit();  | 
|
    /** | 
|
     * Values for the {@code state} bound property. | 
|
     * @since 1.6 | 
|
*/  | 
|
    public enum StateValue { | 
|
        /** | 
|
         * Initial {@code SwingWorker} state. | 
|
*/  | 
|
PENDING,  | 
|
        /** | 
|
         * {@code SwingWorker} is {@code STARTED} | 
|
         * before invoking {@code doInBackground}. | 
|
*/  | 
|
STARTED,  | 
|
        /** | 
|
         * {@code SwingWorker} is {@code DONE} | 
|
         * after {@code doInBackground} method | 
|
         * is finished. | 
|
*/  | 
|
DONE  | 
|
}  | 
|
    /** | 
|
     * Constructs this {@code SwingWorker}. | 
|
*/  | 
|
    public SwingWorker() { | 
|
Callable<T> callable =  | 
|
new Callable<T>() {  | 
|
public T call() throws Exception {  | 
|
setState(StateValue.STARTED);  | 
|
return doInBackground();  | 
|
}  | 
|
};  | 
|
future = new FutureTask<T>(callable) {  | 
|
@Override  | 
|
                       protected void done() { | 
|
doneEDT();  | 
|
setState(StateValue.DONE);  | 
|
}  | 
|
};  | 
|
state = StateValue.PENDING;  | 
|
propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);  | 
|
doProcess = null;  | 
|
doNotifyProgressChange = null;  | 
|
}  | 
|
    /** | 
|
     * Computes a result, or throws an exception if unable to do so. | 
|
     * | 
|
     * <p> | 
|
     * Note that this method is executed only once. | 
|
     * | 
|
     * <p> | 
|
     * Note: this method is executed in a background thread. | 
|
     * | 
|
     * | 
|
     * @return the computed result | 
|
     * @throws Exception if unable to compute a result | 
|
     * | 
|
*/  | 
|
protected abstract T doInBackground() throws Exception ;  | 
|
    /** | 
|
     * Sets this {@code Future} to the result of computation unless | 
|
     * it has been cancelled. | 
|
*/  | 
|
    public final void run() { | 
|
future.run();  | 
|
}  | 
|
    /** | 
|
     * Sends data chunks to the {@link #process} method. This method is to be | 
|
     * used from inside the {@code doInBackground} method to deliver | 
|
     * intermediate results | 
|
     * for processing on the <i>Event Dispatch Thread</i> inside the | 
|
     * {@code process} method. | 
|
     * | 
|
     * <p> | 
|
     * Because the {@code process} method is invoked asynchronously on | 
|
     * the <i>Event Dispatch Thread</i> | 
|
     * multiple invocations to the {@code publish} method | 
|
     * might occur before the {@code process} method is executed. For | 
|
     * performance purposes all these invocations are coalesced into one | 
|
     * invocation with concatenated arguments. | 
|
     * | 
|
     * <p> | 
|
     * For example: | 
|
     * | 
|
     * <pre> | 
|
     * publish("1"); | 
|
     * publish("2", "3"); | 
|
     * publish("4", "5", "6"); | 
|
     * </pre> | 
|
     * | 
|
     * might result in: | 
|
     * | 
|
     * <pre> | 
|
     * process("1", "2", "3", "4", "5", "6") | 
|
     * </pre> | 
|
     * | 
|
     * <p> | 
|
     * <b>Sample Usage</b>. This code snippet loads some tabular data and | 
|
     * updates {@code DefaultTableModel} with it. Note that it safe to mutate | 
|
     * the tableModel from inside the {@code process} method because it is | 
|
     * invoked on the <i>Event Dispatch Thread</i>. | 
|
     * | 
|
     * <pre> | 
|
     * class TableSwingWorker extends | 
|
     *         SwingWorker<DefaultTableModel, Object[]> { | 
|
     *     private final DefaultTableModel tableModel; | 
|
     * | 
|
     *     public TableSwingWorker(DefaultTableModel tableModel) { | 
|
     *         this.tableModel = tableModel; | 
|
     *     } | 
|
     * | 
|
     *     {@code @Override} | 
|
     *     protected DefaultTableModel doInBackground() throws Exception { | 
|
     *         for (Object[] row = loadData(); | 
|
     *                  ! isCancelled() && row != null; | 
|
     *                  row = loadData()) { | 
|
     *             publish((Object[]) row); | 
|
     *         } | 
|
     *         return tableModel; | 
|
     *     } | 
|
     * | 
|
     *     {@code @Override} | 
|
     *     protected void process(List<Object[]> chunks) { | 
|
     *         for (Object[] row : chunks) { | 
|
     *             tableModel.addRow(row); | 
|
     *         } | 
|
     *     } | 
|
     * } | 
|
     * </pre> | 
|
     * | 
|
     * @param chunks intermediate results to process | 
|
     * | 
|
     * @see #process | 
|
     * | 
|
*/  | 
|
@SafeVarargs  | 
|
@SuppressWarnings("varargs") // Passing chunks to add is safe  | 
|
    protected final void publish(V... chunks) { | 
|
        synchronized (this) { | 
|
if (doProcess == null) {  | 
|
doProcess = new AccumulativeRunnable<V>() {  | 
|
@Override  | 
|
public void run(List<V> args) {  | 
|
process(args);  | 
|
}  | 
|
@Override  | 
|
                    protected void submit() { | 
|
doSubmit.add(this);  | 
|
}  | 
|
};  | 
|
}  | 
|
}  | 
|
doProcess.add(chunks);  | 
|
}  | 
|
    /** | 
|
     * Receives data chunks from the {@code publish} method asynchronously on the | 
|
     * <i>Event Dispatch Thread</i>. | 
|
     * | 
|
     * <p> | 
|
     * Please refer to the {@link #publish} method for more details. | 
|
     * | 
|
     * @param chunks intermediate results to process | 
|
     * | 
|
     * @see #publish | 
|
     * | 
|
*/  | 
|
protected void process(List<V> chunks) {  | 
|
}  | 
|
    /** | 
|
     * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground} | 
|
     * method is finished. The default | 
|
     * implementation does nothing. Subclasses may override this method to | 
|
     * perform completion actions on the <i>Event Dispatch Thread</i>. Note | 
|
     * that you can query status inside the implementation of this method to | 
|
     * determine the result of this task or whether this task has been cancelled. | 
|
     * | 
|
     * @see #doInBackground | 
|
     * @see #isCancelled() | 
|
     * @see #get | 
|
*/  | 
|
    protected void done() { | 
|
}  | 
|
    /** | 
|
     * Sets the {@code progress} bound property. | 
|
     * The value should be from 0 to 100. | 
|
     * | 
|
     * <p> | 
|
     * Because {@code PropertyChangeListener}s are notified asynchronously on | 
|
     * the <i>Event Dispatch Thread</i> multiple invocations to the | 
|
     * {@code setProgress} method might occur before any | 
|
     * {@code PropertyChangeListeners} are invoked. For performance purposes | 
|
     * all these invocations are coalesced into one invocation with the last | 
|
     * invocation argument only. | 
|
     * | 
|
     * <p> | 
|
     * For example, the following invokations: | 
|
     * | 
|
     * <pre> | 
|
     * setProgress(1); | 
|
     * setProgress(2); | 
|
     * setProgress(3); | 
|
     * </pre> | 
|
     * | 
|
     * might result in a single {@code PropertyChangeListener} notification with | 
|
     * the value {@code 3}. | 
|
     * | 
|
     * @param progress the progress value to set | 
|
     * @throws IllegalArgumentException is value not from 0 to 100 | 
|
*/  | 
|
    protected final void setProgress(int progress) { | 
|
if (progress < 0 || progress > 100) {  | 
|
throw new IllegalArgumentException("the value should be from 0 to 100");  | 
|
}  | 
|
if (this.progress == progress) {  | 
|
return;  | 
|
}  | 
|
int oldProgress = this.progress;  | 
|
this.progress = progress;  | 
|
if (! getPropertyChangeSupport().hasListeners("progress")) {  | 
|
return;  | 
|
}  | 
|
        synchronized (this) { | 
|
if (doNotifyProgressChange == null) {  | 
|
doNotifyProgressChange =  | 
|
new AccumulativeRunnable<Integer>() {  | 
|
@Override  | 
|
public void run(List<Integer> args) {  | 
|
firePropertyChange("progress",  | 
|
args.get(0),  | 
|
args.get(args.size() - 1));  | 
|
}  | 
|
@Override  | 
|
                        protected void submit() { | 
|
doSubmit.add(this);  | 
|
}  | 
|
};  | 
|
}  | 
|
}  | 
|
doNotifyProgressChange.add(oldProgress, progress);  | 
|
}  | 
|
    /** | 
|
     * Returns the {@code progress} bound property. | 
|
     * | 
|
     * @return the progress bound property. | 
|
*/  | 
|
    public final int getProgress() { | 
|
return progress;  | 
|
}  | 
|
    /** | 
|
     * Schedules this {@code SwingWorker} for execution on a <i>worker</i> | 
|
     * thread. There are a number of <i>worker</i> threads available. In the | 
|
     * event all <i>worker</i> threads are busy handling other | 
|
     * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting | 
|
     * queue. | 
|
     * | 
|
     * <p> | 
|
     * Note: | 
|
     * {@code SwingWorker} is only designed to be executed once.  Executing a | 
|
     * {@code SwingWorker} more than once will not result in invoking the | 
|
     * {@code doInBackground} method twice. | 
|
*/  | 
|
    public final void execute() { | 
|
getWorkersExecutorService().execute(this);  | 
|
}  | 
|
// Future methods START  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
*/  | 
|
    public final boolean cancel(boolean mayInterruptIfRunning) { | 
|
return future.cancel(mayInterruptIfRunning);  | 
|
}  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
*/  | 
|
    public final boolean isCancelled() { | 
|
return future.isCancelled();  | 
|
}  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
*/  | 
|
    public final boolean isDone() { | 
|
return future.isDone();  | 
|
}  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
     * <p> | 
|
     * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks | 
|
     * <i>all</i> events, including repaints, from being processed until this | 
|
     * {@code SwingWorker} is complete. | 
|
     * | 
|
     * <p> | 
|
     * When you want the {@code SwingWorker} to block on the <i>Event | 
|
     * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>. | 
|
     * | 
|
     * <p> | 
|
     * For example: | 
|
     * | 
|
     * <pre> | 
|
     * class SwingWorkerCompletionWaiter extends PropertyChangeListener { | 
|
     *     private JDialog dialog; | 
|
     * | 
|
     *     public SwingWorkerCompletionWaiter(JDialog dialog) { | 
|
     *         this.dialog = dialog; | 
|
     *     } | 
|
     * | 
|
     *     public void propertyChange(PropertyChangeEvent event) { | 
|
     *         if ("state".equals(event.getPropertyName()) | 
|
     *                 && SwingWorker.StateValue.DONE == event.getNewValue()) { | 
|
     *             dialog.setVisible(false); | 
|
     *             dialog.dispose(); | 
|
     *         } | 
|
     *     } | 
|
     * } | 
|
     * JDialog dialog = new JDialog(owner, true); | 
|
     * swingWorker.addPropertyChangeListener( | 
|
     *     new SwingWorkerCompletionWaiter(dialog)); | 
|
     * swingWorker.execute(); | 
|
     * //the dialog will be visible until the SwingWorker is done | 
|
     * dialog.setVisible(true); | 
|
     * </pre> | 
|
*/  | 
|
public final T get() throws InterruptedException, ExecutionException {  | 
|
return future.get();  | 
|
}  | 
|
    /** | 
|
     * {@inheritDoc} | 
|
     * <p> | 
|
     * Please refer to {@link #get} for more details. | 
|
*/  | 
|
public final T get(long timeout, TimeUnit unit) throws InterruptedException,  | 
|
ExecutionException, TimeoutException {  | 
|
return future.get(timeout, unit);  | 
|
}  | 
|
// Future methods END  | 
|
// PropertyChangeSupports methods START  | 
|
    /** | 
|
     * Adds a {@code PropertyChangeListener} to the listener list. The listener | 
|
     * is registered for all properties. The same listener object may be added | 
|
     * more than once, and will be called as many times as it is added. If | 
|
     * {@code listener} is {@code null}, no exception is thrown and no action is taken. | 
|
     * | 
|
     * <p> | 
|
     * Note: This is merely a convenience wrapper. All work is delegated to | 
|
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. | 
|
     * | 
|
     * @param listener the {@code PropertyChangeListener} to be added | 
|
*/  | 
|
public final void addPropertyChangeListener(PropertyChangeListener listener) {  | 
|
getPropertyChangeSupport().addPropertyChangeListener(listener);  | 
|
}  | 
|
    /** | 
|
     * Removes a {@code PropertyChangeListener} from the listener list. This | 
|
     * removes a {@code PropertyChangeListener} that was registered for all | 
|
     * properties. If {@code listener} was added more than once to the same | 
|
     * event source, it will be notified one less time after being removed. If | 
|
     * {@code listener} is {@code null}, or was never added, no exception is | 
|
     * thrown and no action is taken. | 
|
     * | 
|
     * <p> | 
|
     * Note: This is merely a convenience wrapper. All work is delegated to | 
|
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. | 
|
     * | 
|
     * @param listener the {@code PropertyChangeListener} to be removed | 
|
*/  | 
|
public final void removePropertyChangeListener(PropertyChangeListener listener) {  | 
|
getPropertyChangeSupport().removePropertyChangeListener(listener);  | 
|
}  | 
|
    /** | 
|
     * Reports a bound property update to any registered listeners. No event is | 
|
     * fired if {@code old} and {@code new} are equal and non-null. | 
|
     * | 
|
     * <p> | 
|
     * This {@code SwingWorker} will be the source for | 
|
     * any generated events. | 
|
     * | 
|
     * <p> | 
|
     * When called off the <i>Event Dispatch Thread</i> | 
|
     * {@code PropertyChangeListeners} are notified asynchronously on | 
|
     * the <i>Event Dispatch Thread</i>. | 
|
     * <p> | 
|
     * Note: This is merely a convenience wrapper. All work is delegated to | 
|
     * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}. | 
|
     * | 
|
     * | 
|
     * @param propertyName the programmatic name of the property that was | 
|
     *        changed | 
|
     * @param oldValue the old value of the property | 
|
     * @param newValue the new value of the property | 
|
*/  | 
|
public final void firePropertyChange(String propertyName, Object oldValue,  | 
|
Object newValue) {  | 
|
getPropertyChangeSupport().firePropertyChange(propertyName,  | 
|
oldValue, newValue);  | 
|
}  | 
|
    /** | 
|
     * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}. | 
|
     * This method is used when flexible access to bound properties support is | 
|
     * needed. | 
|
     * <p> | 
|
     * This {@code SwingWorker} will be the source for | 
|
     * any generated events. | 
|
     * | 
|
     * <p> | 
|
     * Note: The returned {@code PropertyChangeSupport} notifies any | 
|
     * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch | 
|
     * Thread</i> in the event that {@code firePropertyChange} or | 
|
     * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch | 
|
     * Thread</i>. | 
|
     * | 
|
     * @return {@code PropertyChangeSupport} for this {@code SwingWorker} | 
|
*/  | 
|
public final PropertyChangeSupport getPropertyChangeSupport() {  | 
|
return propertyChangeSupport;  | 
|
}  | 
|
// PropertyChangeSupports methods END  | 
|
    /** | 
|
     * Returns the {@code SwingWorker} state bound property. | 
|
     * | 
|
     * @return the current state | 
|
*/  | 
|
    public final StateValue getState() { | 
|
        /* | 
|
         * DONE is a speacial case | 
|
         * to keep getState and isDone is sync | 
|
*/  | 
|
if (isDone()) {  | 
|
return StateValue.DONE;  | 
|
        } else { | 
|
return state;  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Sets this {@code SwingWorker} state bound property. | 
|
     * @param state the state to set | 
|
*/  | 
|
    private void setState(StateValue state) { | 
|
StateValue old = this.state;  | 
|
this.state = state;  | 
|
firePropertyChange("state", old, state);  | 
|
}  | 
|
    /** | 
|
     * Invokes {@code done} on the EDT. | 
|
*/  | 
|
    private void doneEDT() { | 
|
Runnable doDone =  | 
|
new Runnable() {  | 
|
                public void run() { | 
|
done();  | 
|
}  | 
|
};  | 
|
if (SwingUtilities.isEventDispatchThread()) {  | 
|
doDone.run();  | 
|
        } else { | 
|
doSubmit.add(doDone);  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * returns workersExecutorService. | 
|
     * | 
|
     * returns the service stored in the appContext or creates it if | 
|
     * necessary. | 
|
     * | 
|
     * @return ExecutorService for the {@code SwingWorkers} | 
|
*/  | 
|
private static synchronized ExecutorService getWorkersExecutorService() {  | 
|
final AppContext appContext = AppContext.getAppContext();  | 
|
ExecutorService executorService =  | 
|
(ExecutorService) appContext.get(SwingWorker.class);  | 
|
if (executorService == null) {  | 
|
            //this creates daemon threads. | 
|
ThreadFactory threadFactory =  | 
|
new ThreadFactory() {  | 
|
final ThreadFactory defaultFactory =  | 
|
Executors.defaultThreadFactory();  | 
|
public Thread newThread(final Runnable r) {  | 
|
Thread thread =  | 
|
defaultFactory.newThread(r);  | 
|
thread.setName("SwingWorker-"  | 
|
+ thread.getName());  | 
|
thread.setDaemon(true);  | 
|
return thread;  | 
|
}  | 
|
};  | 
|
executorService =  | 
|
new ThreadPoolExecutor(MAX_WORKER_THREADS, MAX_WORKER_THREADS,  | 
|
10L, TimeUnit.MINUTES,  | 
|
new LinkedBlockingQueue<Runnable>(),  | 
|
threadFactory);  | 
|
appContext.put(SwingWorker.class, executorService);  | 
|
// Don't use ShutdownHook here as it's not enough. We should track  | 
|
            // AppContext disposal instead of JVM shutdown, see 6799345 for details | 
|
final ExecutorService es = executorService;  | 
|
appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME,  | 
|
new PropertyChangeListener() {  | 
|
@Override  | 
|
public void propertyChange(PropertyChangeEvent pce) {  | 
|
boolean disposed = (Boolean)pce.getNewValue();  | 
|
if (disposed) {  | 
|
final WeakReference<ExecutorService> executorServiceRef =  | 
|
new WeakReference<ExecutorService>(es);  | 
|
final ExecutorService executorService =  | 
|
executorServiceRef.get();  | 
|
if (executorService != null) {  | 
|
AccessController.doPrivileged(  | 
|
new PrivilegedAction<Void>() {  | 
|
public Void run() {  | 
|
executorService.shutdown();  | 
|
return null;  | 
|
}  | 
|
}  | 
|
);  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
);  | 
|
}  | 
|
return executorService;  | 
|
}  | 
|
private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit");  | 
|
private static AccumulativeRunnable<Runnable> getDoSubmit() {  | 
|
synchronized (DO_SUBMIT_KEY) {  | 
|
final AppContext appContext = AppContext.getAppContext();  | 
|
Object doSubmit = appContext.get(DO_SUBMIT_KEY);  | 
|
if (doSubmit == null) {  | 
|
doSubmit = new DoSubmitAccumulativeRunnable();  | 
|
appContext.put(DO_SUBMIT_KEY, doSubmit);  | 
|
}  | 
|
return (AccumulativeRunnable<Runnable>) doSubmit;  | 
|
}  | 
|
}  | 
|
private static class DoSubmitAccumulativeRunnable  | 
|
extends AccumulativeRunnable<Runnable> implements ActionListener {  | 
|
private final static int DELAY = 1000 / 30;  | 
|
@Override  | 
|
protected void run(List<Runnable> args) {  | 
|
for (Runnable runnable : args) {  | 
|
runnable.run();  | 
|
}  | 
|
}  | 
|
@Override  | 
|
        protected void submit() { | 
|
Timer timer = new Timer(DELAY, this);  | 
|
timer.setRepeats(false);  | 
|
timer.start();  | 
|
}  | 
|
public void actionPerformed(ActionEvent event) {  | 
|
run();  | 
|
}  | 
|
}  | 
|
private class SwingWorkerPropertyChangeSupport  | 
|
extends PropertyChangeSupport {  | 
|
SwingWorkerPropertyChangeSupport(Object source) {  | 
|
super(source);  | 
|
}  | 
|
@Override  | 
|
public void firePropertyChange(final PropertyChangeEvent evt) {  | 
|
if (SwingUtilities.isEventDispatchThread()) {  | 
|
super.firePropertyChange(evt);  | 
|
            } else { | 
|
doSubmit.add(  | 
|
new Runnable() {  | 
|
                        public void run() { | 
|
SwingWorkerPropertyChangeSupport.this  | 
|
.firePropertyChange(evt);  | 
|
}  | 
|
});  | 
|
}  | 
|
}  | 
|
}  | 
|
}  |