JAL-3048 DialogRunner changes (wip)
[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  * daft gymnastics to allow Dialogs to extend from a Swing class and use this
30  * class to implement chained Response run() definition and execution.
31  * 
32  * There is probably a better way of doing this.
33  * 
34  * @author jprocter
35  *
36  * @param <T>
37  *          the actual dialog that will be shown - which will also initiate the
38  *          response chain.
39  */
40 public class DialogRunner implements DialogRunnerI
41 {
42   private Map<Object, List<RunResponse>> callbacks = new HashMap<>();
43
44   private boolean firstRunWasCalled = false;
45
46   /**
47    * Constructor
48    */
49   public DialogRunner()
50   {
51   }
52
53   /**
54    * Reset so handleResponse will start response execution
55    */
56   public void resetResponses()
57   {
58     firstRunWasCalled = false;
59   }
60
61   @Override
62   public DialogRunnerI addResponse(RunResponse action)
63   {
64     addResponse(false, action);
65     return this;
66   }
67
68   /**
69    * insert a response at the beginning of the chain for the action. Useful to add
70    * pre-action validations local to the Dialog class
71    * 
72    * @param action
73    * @return
74    */
75   public DialogRunnerI setFirstResponse(RunResponse action)
76   {
77     return addResponse(true, action);
78   }
79
80   protected DialogRunnerI addResponse(boolean prePend, RunResponse action)
81   {
82     List<RunResponse> actions = callbacks.get(action.getTrigger());
83     if (actions == null)
84     {
85       actions = new ArrayList<>();
86       callbacks.put(action.getTrigger(), actions);
87     }
88     if (prePend)
89     {
90       actions.add(0,action);
91     } else {
92       actions.add(action);
93     }
94     return this;
95   }
96
97   /**
98    * 
99    * @param action
100    * @return true if action is a registered callback
101    */
102   public boolean isRegistered(RunResponse action)
103   {
104     List<RunResponse> resp = callbacks.get(action.getTrigger());
105     return resp != null && resp.contains(action);
106   }
107
108   /**
109    * Handles a response by running the chain of registered actions (if any).
110    * Answers the list of responses run (in order).
111    * 
112    * @param response
113    */
114   @Override
115   public List<RunResponse> handleResponse(Object response)
116   {
117         List<RunResponse> responsesRun = new ArrayList<RunResponse>();
118
119     /*
120          * this test is for NaN in Chrome
121          */
122     if (response != null && !response.equals(response))
123     {
124       return responsesRun;
125     }
126     
127     /*
128      * failsafe check for illegal duplicate call(?)
129      */
130     if (firstRunWasCalled)
131     {
132 //      return responsesRun;
133     }
134     firstRunWasCalled = true;
135     
136         runResponse(response, responsesRun);
137     if (responsesRun.isEmpty())
138     {
139       System.err.println("Did nothing for " + response);
140     }
141     
142     return responsesRun;
143   }
144
145   /**
146    * Runs any response handlers registered for the given response. If any
147    * response provides a return value, then the handler for that value is
148    * run next recursively. Handlers are only run once.
149    * 
150    * @param response
151  * @param alreadyRun 
152    */
153   private void runResponse(Object response, List<RunResponse> alreadyRun)
154   {
155         /*
156          * this test is for NaN in Chrome
157          */
158     if (response != null && !response.equals(response))
159     {
160       return;
161     }
162     
163     List<RunResponse> actions = response == null ? null : callbacks.get(response);
164
165     if (actions == null)
166     {
167       System.err.println("Doing nothing for " + response);
168       return;
169     }
170     for (RunResponse action : actions)
171     {
172       if (!alreadyRun.contains(action))
173       {
174         action.setReturnValue(null);
175         action.run();
176         alreadyRun.add(action);
177         Object returnValue = action.getReturnValue();
178                 if (returnValue != null)
179         {
180                   /*
181                    * RunResponse wants to chain another action
182                    */
183           runResponse(returnValue, alreadyRun);
184         }
185         break;
186       }
187     }
188   }
189 }