JAL-3048 jalview.utils.dialogrunner.DialogRunner allows sequences of runnable methods...
authorJim Procter <jprocter@issues.jalview.org>
Fri, 29 Jun 2018 15:18:27 +0000 (16:18 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Fri, 29 Jun 2018 15:18:27 +0000 (16:18 +0100)
src/jalview/util/dialogrunner/DialogRunner.java [new file with mode: 0644]
src/jalview/util/dialogrunner/DialogRunnerI.java [new file with mode: 0644]
src/jalview/util/dialogrunner/Response.java [new file with mode: 0644]
src/jalview/util/dialogrunner/RunResponse.java [new file with mode: 0644]
test/jalview/util/dialogrunner/DialogRunnerTest.java [new file with mode: 0644]
test/jalview/util/dialogrunner/ResponseTest.java [new file with mode: 0644]
test/jalview/util/dialogrunner/RunResponseTest.java [new file with mode: 0644]

diff --git a/src/jalview/util/dialogrunner/DialogRunner.java b/src/jalview/util/dialogrunner/DialogRunner.java
new file mode 100644 (file)
index 0000000..fc1a73d
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util.dialogrunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * daft gymnastics to allow Dialogs to extend from a Swing class and use this
+ * class to implement chained Response run() definition and execution.
+ * 
+ * There is probably a better way of doing this.
+ * 
+ * @author jprocter
+ *
+ * @param <T>
+ *          the actual dialog that will be shown - which will also initiate the
+ *          response chain.
+ */
+public class DialogRunner<T extends DialogRunnerI> implements DialogRunnerI
+{
+
+  private Map<Response, RunResponse> callbacks = new java.util.HashMap<>();
+
+  public T dialog;
+
+  public DialogRunner(T ourDialog)
+  {
+    dialog = ourDialog;
+  }
+
+  /**
+   * clear all 'was ran' flags so responses can be called again, and firstRun will
+   * trigger response execution
+   */
+  public void resetResponses()
+  {
+    for (RunResponse response : callbacks.values())
+    {
+      response.reset();
+    }
+    responses.clear();
+    firstRunWasCalled = false;
+  }
+
+  @Override
+  public T response(RunResponse action)
+  {
+    callbacks.put(action.ourTrigger, action);
+    return dialog;
+  }
+
+  /**
+   * handle a response
+   * 
+   * @param responseCode
+   */
+  public void run(int responseCode)
+  {
+    run(new Response(responseCode));
+  }
+
+  public void run(String responseString)
+  {
+    run(new Response(responseString));
+  }
+
+  public void run(Object responseObj)
+  {
+    run(new Response(responseObj));
+  }
+
+  /**
+   * start of response handling.
+   * 
+   * @param responseCode
+   */
+  public void firstRun(int responseCode)
+  {
+    doFirstRun(new Response(responseCode));
+  }
+
+  public void firstRun(String responseString)
+  {
+    doFirstRun(new Response(responseString));
+  }
+
+  public void firstRun(Object responseObj)
+  {
+    doFirstRun(new Response(responseObj));
+  }
+
+
+  boolean firstRunWasCalled = false;
+
+  private void doFirstRun(Response response)
+  {
+    if (firstRunWasCalled)
+    {
+      return;
+    }
+    firstRunWasCalled = true;
+    run(response);
+  }
+
+  private void run(Response response)
+  {
+    responses.add(response);
+    RunResponse action = callbacks.get(response);
+    if (action == null)
+    {
+      System.err.println("Doing nothing for " + response);
+      return;
+    }
+    if (action.wasRun)
+    {
+      System.err
+              .println("IMPLEMENTATION ERROR: Cycle in DialogRunner ! for "
+                      + action);
+    }
+    System.err.println("Executing action for " + response);
+    action.wasRun = true;
+    action.run();
+    if (action.returned != null)
+    {
+      run(action.returned);
+    }
+  }
+
+  List<Response> responses = new ArrayList<>();
+
+}
diff --git a/src/jalview/util/dialogrunner/DialogRunnerI.java b/src/jalview/util/dialogrunner/DialogRunnerI.java
new file mode 100644 (file)
index 0000000..9df6d33
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util.dialogrunner;
+
+/**
+ * functional pattern for blocking dialog response handling
+ * 
+ * @author jprocter
+ *
+ */
+public interface DialogRunnerI<T extends DialogRunnerI>
+{
+
+  /**
+   * define a new response for this dialog. eg. dialog.response(new
+   * RunResponse(OK_PRessed) { run()...}).response(new RunResponse(CANCEL_PRESSED)
+   * { ... });
+   * 
+   * @param action
+   * @return the dialog
+   */
+  T response(RunResponse action);
+
+}
diff --git a/src/jalview/util/dialogrunner/Response.java b/src/jalview/util/dialogrunner/Response.java
new file mode 100644 (file)
index 0000000..15df8f8
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util.dialogrunner;
+
+public class Response
+{
+  int type = 0; // int = 0, String = 1, Object = 2;
+
+  int intresp;
+
+  String stringresp;
+
+  Object objresp;
+
+  public Response(int response)
+  {
+    type = 0;
+    intresp = response;
+  }
+
+  public Response(String response)
+  {
+    type = 1;
+    stringresp = response;
+  }
+
+  public Response(Object response)
+  {
+    if (response instanceof String)
+    {
+      type = 1;
+      stringresp = (String) response;
+      return;
+    }
+    if (response instanceof Integer)
+    {
+      type = 0;
+      intresp = ((Integer) response).intValue();
+      return;
+    }
+    objresp = response;
+    type = 2;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (obj == null || !(obj instanceof Response))
+    {
+      return false;
+    }
+    ;
+    if (((Response) obj).type == type)
+    {
+      switch (type)
+      {
+      case 0:
+        return ((((Response) obj).intresp) == intresp);
+      case 1:
+        return (((Response) obj).stringresp.equals(stringresp));
+      case 2:
+        return (((Response) obj).objresp).equals(objresp);
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode()
+  {
+    switch (type)
+    {
+    case 0:
+      return Integer.valueOf(intresp).hashCode();
+    case 1:
+      return stringresp.hashCode();
+    case 2:
+      return objresp.hashCode();
+    }
+    return super.hashCode();
+  }
+
+  @Override
+  public String toString()
+  {
+    switch (type)
+    {
+    case 0:
+      return "DialogRunner int: " + intresp;
+    case 1:
+      return "DialogRunner str: '" + stringresp + "'";
+    case 2:
+      return "DialogRunner obj: " + objresp.toString();
+    }
+    return "Unconfigured response.";
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/util/dialogrunner/RunResponse.java b/src/jalview/util/dialogrunner/RunResponse.java
new file mode 100644 (file)
index 0000000..acc53f0
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util.dialogrunner;
+
+/**
+ * A Runnable that is kind of like a future, that allows a sequence of Runabbles
+ * to be conditionally executed by DialogRunner
+ * 
+ * @author jprocter
+ *
+ */
+public abstract class RunResponse implements Runnable
+{
+  /**
+   * Response that triggers the Run method
+   */
+  public Response ourTrigger;
+
+  /**
+   * set by run() on exit
+   */
+  public Response returned = null;
+
+  /**
+   * set by dialog runner
+   */
+  public boolean wasRun = false;
+
+  public RunResponse(int trigger)
+  {
+    ourTrigger = new Response(trigger);
+  }
+
+  public RunResponse(Object trigger)
+  {
+    ourTrigger = new Response(trigger);
+  }
+
+  public RunResponse(String trigger)
+  {
+    ourTrigger = new Response(trigger);
+  }
+
+  public RunResponse(Response trigger)
+  {
+    ourTrigger = trigger;
+  }
+
+  public void reset()
+  {
+    wasRun = false;
+    returned = null;
+
+  }
+
+  @Override
+  public String toString()
+  {
+    return "Runner for " + ourTrigger;
+  }
+}
diff --git a/test/jalview/util/dialogrunner/DialogRunnerTest.java b/test/jalview/util/dialogrunner/DialogRunnerTest.java
new file mode 100644 (file)
index 0000000..e956124
--- /dev/null
@@ -0,0 +1,101 @@
+package jalview.util.dialogrunner;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class DialogRunnerTest
+{
+  public class MockDialog implements DialogRunnerI
+  {
+    DialogRunner<MockDialog> runner = new DialogRunner<>(this);
+
+    @Override
+    public MockDialog response(RunResponse action)
+    {
+      return runner.response(action);
+    }
+
+    public void doDialog(String resp)
+    {
+      runner.firstRun(resp);
+    }
+  }
+
+  MockDialog dialog = new MockDialog();
+
+  @Test
+  public void testDialogRunner()
+  {
+    RunResponse ok, cancel, help, ineed;
+    final Response ooh = new Response("OOOOoooOOOOH!"),
+            r_ok = new Response("OK"), r_cancel = new Response("CANCEL"),
+            r_done = new Response("DONE"), r_help = new Response("HELP"),
+            r_ddoit = new Response("DIDNT DOIT"),
+            r_needsb = new Response("I NEED SOMEBODY");
+
+    ok = new RunResponse("OK")
+    {
+
+      @Override
+      public void run()
+      {
+        returned = new Response("DONE");
+      }
+    };
+    cancel = new RunResponse("CANCEL")
+    {
+      @Override
+      public void run()
+      {
+        returned = r_ddoit;
+      }
+    };
+    help = new RunResponse("HELP")
+    {
+      @Override
+      public void run()
+      {
+        returned = r_needsb;
+
+      }
+    };
+    ineed = new RunResponse(r_needsb)
+    {
+      @Override
+      public void run()
+      {
+        returned = ooh;
+      }
+    };
+
+    dialog.response(ok).response(cancel).response(help).response(ineed);
+
+    Assert.assertFalse(dialog.runner.firstRunWasCalled);
+    dialog.doDialog("OK");
+    // OK called, nothing else.
+    Assert.assertTrue(dialog.runner.firstRunWasCalled);
+    Assert.assertTrue(ok.wasRun);
+    Assert.assertEquals(ok.returned, r_done);
+    Assert.assertFalse(cancel.wasRun);
+    Assert.assertEquals(dialog.runner.responses.size(), 2);
+
+    // do it again - check it doesn't trigger again
+    ok.wasRun = false;
+    dialog.doDialog("OK");
+    Assert.assertFalse(ok.wasRun);
+
+    // reset - everything false/null
+    dialog.runner.resetResponses();
+    Assert.assertFalse(dialog.runner.firstRunWasCalled);
+    Assert.assertFalse(ok.wasRun);
+    Assert.assertNull(ok.returned);
+    Assert.assertEquals(dialog.runner.responses.size(), 0);
+
+    // cancel called ..
+    dialog.doDialog("HELP");
+    Assert.assertTrue(dialog.runner.firstRunWasCalled);
+    Assert.assertFalse(ok.wasRun);
+    Assert.assertEquals(ineed.returned, ooh);
+    Assert.assertEquals(dialog.runner.responses.size(), 3);
+  }
+}
diff --git a/test/jalview/util/dialogrunner/ResponseTest.java b/test/jalview/util/dialogrunner/ResponseTest.java
new file mode 100644 (file)
index 0000000..d722b1a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util.dialogrunner;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ResponseTest
+{
+  @Test
+  public void testResonse() {
+    Response intr=new Response(1),intrCopy=new Response(1);
+    Response strr=new Response("1"),strrcopy=new Response("1");
+    Response objr=new Response(Double.valueOf(1d));
+    Assert.assertTrue(intr.equals(intrCopy));
+    Assert.assertTrue(strr.equals(strrcopy));
+    Assert.assertFalse(intr.equals(strr));
+    Assert.assertFalse(intr.equals(objr));
+    Assert.assertFalse(strr.equals(objr));
+    Assert.assertEquals(intr.toString(), "DialogRunner int: 1");
+    Assert.assertEquals(strr.toString(), "DialogRunner str: '1'");
+    Assert.assertEquals(objr.toString(), "DialogRunner obj: 1.0");
+  }
+}
diff --git a/test/jalview/util/dialogrunner/RunResponseTest.java b/test/jalview/util/dialogrunner/RunResponseTest.java
new file mode 100644 (file)
index 0000000..ff86675
--- /dev/null
@@ -0,0 +1,57 @@
+package jalview.util.dialogrunner;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class RunResponseTest
+{
+  @Test
+  public void testRunResponse()
+  {
+
+    RunResponse rr = new RunResponse("OK")
+    {
+      @Override
+      public void run()
+      {
+        returned = new Response("DONE");
+      }
+    };
+    Assert.assertEquals(rr.ourTrigger, new Response("OK"));
+    Assert.assertNotEquals(rr.ourTrigger, new Response("NOTOK"));
+    Assert.assertNull(rr.returned);
+    Assert.assertFalse(rr.wasRun);
+    // trivial ..
+    rr.wasRun = true;
+    rr.run();
+    Assert.assertTrue(rr.wasRun);
+
+    Assert.assertEquals(rr.returned, new Response("DONE"));
+    rr.reset();
+    Assert.assertNull(rr.returned);
+    Assert.assertFalse(rr.wasRun);
+
+    Assert.assertEquals(rr.toString(), "Runner for " + new Response("OK"));
+
+    // just test the other constructors
+    RunResponse rr12 = new RunResponse(12)
+    {
+      @Override
+      public void run()
+      {
+        returned = new Response("DONE");
+      }
+    };
+    RunResponse rrpi = new RunResponse(new Double(3.142))
+    {
+      @Override
+      public void run()
+      {
+        returned = new Response("DONE");
+      }
+    };
+    Assert.assertEquals(rr12.ourTrigger, new Response(12));
+    Assert.assertEquals(rrpi.ourTrigger,
+            new Response(Double.valueOf(3.142)));
+}
+}