+ frame.pack();
+ }
+
+ /*
+ * THREAD HANDLING
+ *
+ * Special measures are needed because:
+ * - it is not permitted to use any Swing UI classes or objects
+ * other than on the Swing event thread
+ * - we want to run our upload asynchronously
+ * - we want to do some computation asynchronously (eg, the
+ * arbitrage and upload data prep)
+ *
+ * So what we do is this:
+ * 1. When the user asks to upload, we spawn a new thread
+ * to do the upload ("MarketUploader-uploader-*", see
+ * the call to "new Thread" inside createGUI.
+ * 2. Whenever that thread needs to touch a UI object it
+ * uses EventQueue.invokeLater or .invokeAndWait to
+ * perform the relevant action. We wrap these calls up
+ * in three utility classes:
+ * UIA - runs code on UI thread, asynchronously
+ * UIX - runs code on UI thread, waits for it to finish
+ * UIXR - as UIX but also returns a value
+ * These hide the details of the EventQueue class and also do
+ * some debugging and argument shuffling; the calling syntax is
+ * still painful, unfortunately, and there is a weird constraint
+ * on variables used inside the inner body. For a simple
+ * example, see the handling of "summary" and "summary_final"
+ * for the call to UIX at the bottom of runUpload.
+ * 3. Try to put everything back when that thread exits.
+ *
+ * Additionally:
+ * a. There is another thread spawed early to get a timestamp from
+ * YARRG, if we are uploading there.
+ * b. Finding the island name can involve callbacks which run in
+ * the UI event thread. Basically we do the work there, and use
+ * a CountDownLatch to cause the uploader thread to wait as
+ * appropriate.
+ */
+
+ private void on_ui_thread() { assert(EventQueue.isDispatchThread()); }
+ private void on_our_thread() { assert(!EventQueue.isDispatchThread()); }
+
+ private abstract class UIA implements Runnable {
+ private String what;
+ public abstract void body();
+ public void run() {
+ debuglog("UIA 2 "+what+" begin");
+ body();
+ debuglog("UIA 3 "+what+" done");
+ }
+ public void exec(String what_in) {
+ what = what_in;
+ debuglog("UIA 1 "+what+" request");
+ EventQueue.invokeLater(this);
+ }
+ };
+ private abstract class UIXR<ReturnType> implements Runnable {
+ public abstract ReturnType bodyr();
+ public ReturnType return_value;
+ private String what;
+ public void run() {
+ debuglog("UIX 2 "+what+" begin");
+ return_value = bodyr();
+ debuglog("UIX 3 "+what+" done");
+ }
+ public ReturnType exec(String what_in) throws Exception {
+ what = what_in;
+ if (EventQueue.isDispatchThread()) {
+ debuglog("UIX 1 "+what+" (event thread) entry");
+ this.run();
+ debuglog("UIX 4 "+what+" (event thread) exit");
+ } else {
+ debuglog("UIX 1 "+what+" (other thread) entry");
+ EventQueue.invokeAndWait(this);
+ debuglog("UIX 4 "+what+" (other thread) exit");
+ }
+ return return_value;
+ }
+ };
+ private abstract class UIX extends UIXR<Object> implements Runnable {
+ public abstract void body();
+ public Object bodyr() { body(); return null; }
+ };
+
+ /*
+ * ERROR REPORTING AND GENERAL UTILITIES
+ *
+ * Synchronous modal dialogues
+ * error and error_html may be called from any thread
+ */
+
+ private void error(final String msg) {
+ try {
+ new UIX() { public void body() {
+ resultSummary.setText("failed");
+ JOptionPane.showMessageDialog(frame,msg,"Error",
+ JOptionPane.ERROR_MESSAGE);
+ }}.exec("error()");
+ } catch (Exception e) {
+ System.err.println("exception reporting to UI thread:");
+ e.printStackTrace();
+ }
+ }
+
+ private void error_html(final String msg, String html) {
+ Pattern body = Pattern.compile("<body>(.*)</body>",
+ Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
+ Matcher m = body.matcher(html);
+ if (m.find()) {
+ html = m.group(1);
+ Pattern fixup = Pattern.compile("<(\\w+) */>");;
+ m = fixup.matcher(html);
+ html = m.replaceAll("<$1>");
+ m = Pattern.compile("[\\r\\n]+").matcher(html);
+ html = m.replaceAll(" ");
+ }
+ String whole_msg = "<html><h1>Error</h1>"+msg
+ +"<h1>PCTB Server said:</h1><blockquote>"+html+"</blockquote>";
+ debuglog("###" + whole_msg + "###");
+
+ error(whole_msg);
+ }
+
+ private void setProgress(final int nv) throws Exception {
+ new UIA() { public void body() {
+ progmon.setProgress(nv);
+ }}.exec("setProgress "+nv);
+ }
+ private boolean checkCancelled() throws Exception {
+ return new UIXR<Boolean>() { public Boolean bodyr() {
+ boolean can = progmon.isCanceled();
+ if (can) resultSummary.setText("cancelled");
+ return new Boolean(can);
+ }}.exec("checkCancelled").booleanValue();
+ }