001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import java.awt.Component; 005import java.awt.EventQueue; 006import java.io.IOException; 007 008import javax.swing.SwingUtilities; 009 010import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 011import org.openstreetmap.josm.gui.progress.ProgressMonitor; 012import org.openstreetmap.josm.gui.progress.ProgressMonitor.CancelListener; 013import org.openstreetmap.josm.gui.progress.ProgressTaskId; 014import org.openstreetmap.josm.io.OsmTransferException; 015import org.openstreetmap.josm.tools.BugReportExceptionHandler; 016import org.openstreetmap.josm.tools.CheckParameterUtil; 017import 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 */ 025public 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 @Override 089 public void run() { 090 finish(); 091 } 092 }); 093 } 094 } 095 } finally { 096 progressMonitor.finishTask(); 097 progressMonitor.removeCancelListener(this); 098 progressMonitor.setProgressTaskId(oldTaskId); 099 if (progressMonitor instanceof PleaseWaitProgressMonitor) { 100 ((PleaseWaitProgressMonitor)progressMonitor).close(); 101 } 102 if (EventQueue.isDispatchThread()) { 103 afterFinish(); 104 } else { 105 EventQueue.invokeAndWait(new Runnable() { 106 @Override 107 public void run() { 108 afterFinish(); 109 } 110 }); 111 } 112 } 113 } catch (final Exception e) { 114 if (!ignoreException) { 115 // Exception has to thrown in EDT to be shown to user 116 SwingUtilities.invokeLater(new Runnable() { 117 @Override 118 public void run() { 119 if (e instanceof RuntimeException) { 120 BugReportExceptionHandler.handleException(e); 121 } else { 122 ExceptionDialogUtil.explainException(e); 123 } 124 } 125 }); 126 } 127 } 128 } 129 130 /** 131 * Can be overriden if something needs to run after progress monitor is closed. 132 */ 133 protected void afterFinish() { 134 135 } 136 137 @Override 138 public final void run() { 139 if (canceled) 140 return; // since realRun isn't executed, do not call to finish 141 142 if (EventQueue.isDispatchThread()) { 143 new Thread(new Runnable() { 144 @Override 145 public void run() { 146 doRealRun(); 147 } 148 }).start(); 149 } else { 150 doRealRun(); 151 } 152 } 153 154 @Override 155 public void operationCanceled() { 156 cancel(); 157 } 158 159 /** 160 * User pressed cancel button. 161 */ 162 protected abstract void cancel(); 163 164 /** 165 * Called in the worker thread to do the actual work. When any of the 166 * exception is thrown, a message box will be displayed and closeDialog 167 * is called. finish() is called in any case. 168 */ 169 protected abstract void realRun() throws SAXException, IOException, OsmTransferException; 170 171 /** 172 * Finish up the data work. Is guaranteed to be called if realRun is called. 173 * Finish is called in the gui thread just after the dialog disappeared. 174 */ 175 protected abstract void finish(); 176 177 public ProgressMonitor getProgressMonitor() { 178 return progressMonitor; 179 } 180 181 /** 182 * Task can run in background if returned value != null. Note that it's tasks responsibility 183 * to ensure proper synchronization, PleaseWaitRunnable doesn't with it. 184 * @return If returned value is != null then task can run in background. TaskId could be used in future for "Always run in background" checkbox 185 */ 186 public ProgressTaskId canRunInBackground() { 187 return null; 188 } 189}