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 * @param title message for the user 035 */ 036 public PleaseWaitRunnable(String title) { 037 this(title, false); 038 } 039 040 /** 041 * Create the runnable object with a given message for the user. 042 * 043 * @param title message for the user 044 * @param ignoreException If true, exception will be silently ignored. If false then 045 * exception will be handled by showing a dialog. When this runnable is executed using executor framework 046 * then use false unless you read result of task (because exception will get lost if you don't) 047 */ 048 public PleaseWaitRunnable(String title, boolean ignoreException) { 049 this(title, new PleaseWaitProgressMonitor(title), ignoreException); 050 } 051 052 /** 053 * Create the runnable object with a given message for the user 054 * 055 * @param parent the parent component for the please wait dialog. Must not be null. 056 * @param title message for the user 057 * @param ignoreException If true, exception will be silently ignored. If false then 058 * exception will be handled by showing a dialog. When this runnable is executed using executor framework 059 * then use false unless you read result of task (because exception will get lost if you don't) 060 * @throws IllegalArgumentException thrown if parent is null 061 */ 062 public PleaseWaitRunnable(Component parent, String title, boolean ignoreException) throws IllegalArgumentException{ 063 CheckParameterUtil.ensureParameterNotNull(parent, "parent"); 064 this.title = title; 065 this.progressMonitor = new PleaseWaitProgressMonitor(parent, title); 066 this.ignoreException = ignoreException; 067 } 068 069 /** 070 * Create the runnable object with a given message for the user 071 * 072 * @param title message for the user 073 * @param progressMonitor progress monitor 074 * @param ignoreException If true, exception will be silently ignored. If false then 075 * exception will be handled by showing a dialog. When this runnable is executed using executor framework 076 * then use false unless you read result of task (because exception will get lost if you don't) 077 */ 078 public PleaseWaitRunnable(String title, ProgressMonitor progressMonitor, boolean ignoreException) { 079 this.title = title; 080 this.progressMonitor = progressMonitor == null?new PleaseWaitProgressMonitor(title):progressMonitor; 081 this.ignoreException = ignoreException; 082 } 083 084 private void doRealRun() { 085 try { 086 ProgressTaskId oldTaskId = null; 087 try { 088 progressMonitor.addCancelListener(this); 089 progressMonitor.beginTask(title); 090 oldTaskId = progressMonitor.getProgressTaskId(); 091 progressMonitor.setProgressTaskId(canRunInBackground()); 092 try { 093 realRun(); 094 } finally { 095 if (EventQueue.isDispatchThread()) { 096 finish(); 097 } else { 098 EventQueue.invokeAndWait(new Runnable() { 099 @Override 100 public void run() { 101 finish(); 102 } 103 }); 104 } 105 } 106 } finally { 107 progressMonitor.finishTask(); 108 progressMonitor.removeCancelListener(this); 109 progressMonitor.setProgressTaskId(oldTaskId); 110 if (progressMonitor instanceof PleaseWaitProgressMonitor) { 111 ((PleaseWaitProgressMonitor)progressMonitor).close(); 112 } 113 if (EventQueue.isDispatchThread()) { 114 afterFinish(); 115 } else { 116 EventQueue.invokeAndWait(new Runnable() { 117 @Override 118 public void run() { 119 afterFinish(); 120 } 121 }); 122 } 123 } 124 } catch (final Exception e) { 125 if (!ignoreException) { 126 // Exception has to thrown in EDT to be shown to user 127 SwingUtilities.invokeLater(new Runnable() { 128 @Override 129 public void run() { 130 if (e instanceof RuntimeException) { 131 BugReportExceptionHandler.handleException(e); 132 } else { 133 ExceptionDialogUtil.explainException(e); 134 } 135 } 136 }); 137 } 138 } 139 } 140 141 /** 142 * Can be overriden if something needs to run after progress monitor is closed. 143 */ 144 protected void afterFinish() { 145 146 } 147 148 @Override 149 public final void run() { 150 if (canceled) 151 return; // since realRun isn't executed, do not call to finish 152 153 if (EventQueue.isDispatchThread()) { 154 new Thread(new Runnable() { 155 @Override 156 public void run() { 157 doRealRun(); 158 } 159 }).start(); 160 } else { 161 doRealRun(); 162 } 163 } 164 165 @Override 166 public void operationCanceled() { 167 cancel(); 168 } 169 170 /** 171 * User pressed cancel button. 172 */ 173 protected abstract void cancel(); 174 175 /** 176 * Called in the worker thread to do the actual work. When any of the 177 * exception is thrown, a message box will be displayed and closeDialog 178 * is called. finish() is called in any case. 179 */ 180 protected abstract void realRun() throws SAXException, IOException, OsmTransferException; 181 182 /** 183 * Finish up the data work. Is guaranteed to be called if realRun is called. 184 * Finish is called in the gui thread just after the dialog disappeared. 185 */ 186 protected abstract void finish(); 187 188 /** 189 * Relies the progress monitor. 190 * @return the progress monitor 191 */ 192 public ProgressMonitor getProgressMonitor() { 193 return progressMonitor; 194 } 195 196 /** 197 * Task can run in background if returned value != null. Note that it's tasks responsibility 198 * to ensure proper synchronization, PleaseWaitRunnable doesn't with it. 199 * @return If returned value is != null then task can run in background. TaskId could be used in future for "Always run in background" checkbox 200 */ 201 public ProgressTaskId canRunInBackground() { 202 return null; 203 } 204}