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 }