Merge branch 'Jalview-BH/JAL-3026-JAL-3063-JAXB' into
[jalview.git] / src / jalview / util / dialogrunner / DialogRunner.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  *
5  * This file is part of Jalview.
6  *
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * Jalview is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE.  See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.util.dialogrunner;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 /**
29  * A helper class that executes registered Runnable actions corresponding to 
30  * user responses in a dialog. This is to enable dialog execution in JalviewJS,
31  * where everything must happen in a single thread of execution. That is, the
32  * dialog has to 'know' all actions that follow a user choice, rather than
33  * returning a response to allow a separate thread to decide the next action.
34  * 
35  * @author jprocter
36  */
37 public class DialogRunner implements DialogRunnerI
38 {
39   private Map<Object, List<RunResponse>> callbacks = new HashMap<>();
40
41   /**
42    * Constructor
43    */
44   public DialogRunner()
45   {
46   }
47
48   @Override
49   public DialogRunnerI addResponse(Object response, RunResponse action)
50   {
51         List<RunResponse> actions = callbacks.get(response);
52     if (actions == null)
53     {
54       actions = new ArrayList<>();
55       callbacks.put(response, actions);
56     }
57     actions.add(action);
58     return this;
59   }
60   
61   /**
62    * Handles a response by running the chain of registered actions (if any).
63    * Answers the list of responses run (in order).
64    * 
65    * @param response
66    */
67   @Override
68   public List<RunResponse> handleResponse(Object response)
69   {
70         List<RunResponse> responsesRun = new ArrayList<RunResponse>();
71
72     /*
73          * this test is for NaN in Chrome
74          */
75     if (response != null && !response.equals(response))
76     {
77       return responsesRun;
78     }
79     
80         runResponse(response, responsesRun);
81     if (responsesRun.isEmpty())
82     {
83       System.err.println("Did nothing for " + response);
84     }
85     
86     return responsesRun;
87   }
88
89   /**
90    * Runs any response handlers registered for the given response. If any
91    * response provides a return value, then the handler for that value is
92    * run next recursively. Handlers are only run once.
93    * 
94    * @param response
95  * @param alreadyRun 
96    */
97   private void runResponse(Object response, List<RunResponse> alreadyRun)
98   {
99         /*
100          * this test is for NaN in Chrome
101          */
102     if (response != null && !response.equals(response))
103     {
104       return;
105     }
106     
107     List<RunResponse> actions = response == null ? null : callbacks.get(response);
108
109     if (actions == null)
110     {
111       System.err.println("Doing nothing for " + response);
112       return;
113     }
114     for (RunResponse action : actions)
115     {
116       if (!alreadyRun.contains(action))
117       {
118         action.setReturnValue(null);
119         action.run();
120         alreadyRun.add(action);
121         Object returnValue = action.getReturnValue();
122                 if (returnValue != null)
123         {
124                   /*
125                    * RunResponse wants to chain another action
126                    */
127           runResponse(returnValue, alreadyRun);
128         }
129         break;
130       }
131     }
132   }
133 }