001 // License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.gui;
003
004 import java.awt.Component;
005 import java.awt.EventQueue;
006 import java.io.IOException;
007
008 import javax.swing.SwingUtilities;
009
010 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
011 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
012 import org.openstreetmap.josm.gui.progress.ProgressMonitor.CancelListener;
013 import org.openstreetmap.josm.gui.progress.ProgressTaskId;
014 import org.openstreetmap.josm.io.OsmTransferException;
015 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
016 import org.openstreetmap.josm.tools.CheckParameterUtil;
017 import org.xml.sax.SAXException;
018
019 /**
020 * Instanced of this thread will display a "Please Wait" message in middle of JOSM
021 * to indicate a progress being executed.
022 *
023 * @author Imi
024 */
025 public abstract class PleaseWaitRunnable implements Runnable, CancelListener {
026 private boolean canceled = false;
027 private boolean ignoreException;
028 private final String title;
029
030 protected final ProgressMonitor progressMonitor;
031
032 /**
033 * Create the runnable object with a given message for the user.
034 */
035 public PleaseWaitRunnable(String title) {
036 this(title, false);
037 }
038 /**
039 * Create the runnable object with a given message for the user.
040 *
041 * @param title message for the user
042 * @param ignoreException If true, exception will be propagated to calling code. If false then
043 * exception will be thrown directly in EDT. When this runnable is executed using executor framework
044 * then use false unless you read result of task (because exception will get lost if you don't)
045 */
046 public PleaseWaitRunnable(String title, boolean ignoreException) {
047 this(title, new PleaseWaitProgressMonitor(title), ignoreException);
048 }
049
050 /**
051 * Create the runnable object with a given message for the user
052 *
053 * @param parent the parent component for the please wait dialog. Must not be null.
054 * @param title message for the user
055 * @param ignoreException If true, exception will be propagated to calling code. If false then
056 * exception will be thrown directly in EDT. When this runnable is executed using executor framework
057 * then use false unless you read result of task (because exception will get lost if you don't)
058 * @throws IllegalArgumentException thrown if parent is null
059 */
060 public PleaseWaitRunnable(Component parent, String title, boolean ignoreException) throws IllegalArgumentException{
061 CheckParameterUtil.ensureParameterNotNull(parent, "parent");
062 this.title = title;
063 this.progressMonitor = new PleaseWaitProgressMonitor(parent, title);
064 this.ignoreException = ignoreException;
065 }
066
067 public PleaseWaitRunnable(String title, ProgressMonitor progressMonitor, boolean ignoreException) {
068 this.title = title;
069 this.progressMonitor = progressMonitor == null?new PleaseWaitProgressMonitor(title):progressMonitor;
070 this.ignoreException = ignoreException;
071 }
072
073 private void doRealRun() {
074 try {
075 ProgressTaskId oldTaskId = null;
076 try {
077 progressMonitor.addCancelListener(this);
078 progressMonitor.beginTask(title);
079 oldTaskId = progressMonitor.getProgressTaskId();
080 progressMonitor.setProgressTaskId(canRunInBackground());
081 try {
082 realRun();
083 } finally {
084 if (EventQueue.isDispatchThread()) {
085 finish();
086 } else {
087 EventQueue.invokeAndWait(new Runnable() {
088 public void run() {
089 finish();
090 }
091 });
092 }
093 }
094 } finally {
095 progressMonitor.finishTask();
096 progressMonitor.removeCancelListener(this);
097 progressMonitor.setProgressTaskId(oldTaskId);
098 if (progressMonitor instanceof PleaseWaitProgressMonitor) {
099 ((PleaseWaitProgressMonitor)progressMonitor).close();
100 }
101 if (EventQueue.isDispatchThread()) {
102 afterFinish();
103 } else {
104 EventQueue.invokeAndWait(new Runnable() {
105 public void run() {
106 afterFinish();
107 }
108 });
109 }
110 }
111 } catch (final Exception e) {
112 if (!ignoreException) {
113 // Exception has to thrown in EDT to be shown to user
114 SwingUtilities.invokeLater(new Runnable() {
115 public void run() {
116 if (e instanceof RuntimeException) {
117 BugReportExceptionHandler.handleException(e);
118 } else {
119 ExceptionDialogUtil.explainException(e);
120 }
121 }
122 });
123 }
124 }
125 }
126
127 /**
128 * Can be overriden if something needs to run after progress monitor is closed.
129 */
130 protected void afterFinish() {
131
132 }
133
134 public final void run() {
135 if (canceled)
136 return; // since realRun isn't executed, do not call to finish
137
138 if (EventQueue.isDispatchThread()) {
139 new Thread(new Runnable() {
140 public void run() {
141 doRealRun();
142 }
143 }).start();
144 } else {
145 doRealRun();
146 }
147 }
148
149 public void operationCanceled() {
150 cancel();
151 }
152
153 /**
154 * User pressed cancel button.
155 */
156 protected abstract void cancel();
157
158 /**
159 * Called in the worker thread to do the actual work. When any of the
160 * exception is thrown, a message box will be displayed and closeDialog
161 * is called. finish() is called in any case.
162 */
163 protected abstract void realRun() throws SAXException, IOException, OsmTransferException;
164
165 /**
166 * Finish up the data work. Is guaranteed to be called if realRun is called.
167 * Finish is called in the gui thread just after the dialog disappeared.
168 */
169 protected abstract void finish();
170
171 public ProgressMonitor getProgressMonitor() {
172 return progressMonitor;
173 }
174
175 /**
176 * Task can run in background if returned value <> null. Note that it's tasks responsibility
177 * to ensure proper synchronization, PleaseWaitRunnable doesn't with it.
178 * @return If returned value is <> null then task can run in background. TaskId could be used in future for "Always run in background" checkbox
179 */
180 public ProgressTaskId canRunInBackground() {
181 return null;
182 }
183 }