mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-18 18:14:38 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
869
src/java.desktop/share/classes/javax/swing/SwingWorker.java
Normal file
869
src/java.desktop/share/classes/javax/swing/SwingWorker.java
Normal file
|
@ -0,0 +1,869 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2017, 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="http://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 implements 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);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
AccumulativeRunnable<Runnable> tmp = (AccumulativeRunnable<Runnable>) doSubmit;
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
private static class DoSubmitAccumulativeRunnable
|
||||
extends AccumulativeRunnable<Runnable> implements ActionListener {
|
||||
private static final 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();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial") // JDK-implementation class
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue