JAL-4199 Remove taskRestarted from task events
[jalview.git] / src / jalview / ws2 / gui / AlignmentServiceGuiHandler.java
1 package jalview.ws2.gui;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.BitSet;
6 import java.util.HashMap;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedList;
9 import java.util.List;
10
11 import javax.swing.JInternalFrame;
12 import javax.swing.SwingUtilities;
13
14 import jalview.bin.Cache;
15 import jalview.bin.Console;
16 import jalview.datamodel.Alignment;
17 import jalview.datamodel.AlignmentI;
18 import jalview.datamodel.AlignmentOrder;
19 import jalview.datamodel.HiddenColumns;
20 import jalview.gui.AlignFrame;
21 import jalview.gui.Desktop;
22 import jalview.gui.JvOptionPane;
23 import jalview.gui.SplitFrame;
24 import jalview.gui.WebserviceInfo;
25 import jalview.util.ArrayUtils;
26 import jalview.util.MessageManager;
27 import jalview.util.Pair;
28 import jalview.ws2.actions.alignment.AlignmentAction;
29 import jalview.ws2.actions.alignment.AlignmentResult;
30 import jalview.ws2.actions.api.JobI;
31 import jalview.ws2.actions.api.TaskEventListener;
32 import jalview.ws2.actions.api.TaskI;
33 import jalview.ws2.api.JobStatus;
34 import jalview.ws2.api.WebService;
35 import jalview.ws2.helpers.WSClientTaskWrapper;
36
37 class AlignmentServiceGuiHandler
38     implements TaskEventListener<AlignmentResult>
39 {
40   private final WebService<?> service;
41
42   private final AlignFrame frame;
43
44   private WebserviceInfo infoPanel;
45
46   private String alnTitle; // title of the alignment used in new window
47
48   private JobI[] jobs = new JobI[0];
49
50   private int[] tabs = new int[0];
51
52   private int[] logOffset = new int[0];
53
54   private int[] errLogOffset = new int[0];
55
56   public AlignmentServiceGuiHandler(AlignmentAction action, AlignFrame frame)
57   {
58     this.service = action.getWebService();
59     this.frame = frame;
60     String panelInfo = String.format("%s using service hosted at %s%n%s",
61         service.getName(), service.getUrl(), service.getDescription());
62     infoPanel = new WebserviceInfo(service.getName(), panelInfo, false);
63     String actionName = action.getName() != null ? action.getName() : "Alignment";
64     alnTitle = String.format("%s %s of %s", service.getName(), actionName,
65         frame.getTitle());
66   }
67
68   @Override
69   public void taskStatusChanged(TaskI<AlignmentResult> source, JobStatus status)
70   {
71     switch (status)
72     {
73     case INVALID:
74       infoPanel.setVisible(false);
75       JvOptionPane.showMessageDialog(frame,
76           MessageManager.getString("info.invalid_msa_input_mininfo"),
77           MessageManager.getString("info.invalid_msa_notenough"),
78           JvOptionPane.INFORMATION_MESSAGE);
79       break;
80     case READY:
81       infoPanel.setthisService(new WSClientTaskWrapper(source));
82       infoPanel.setVisible(true);
83       // intentional no break
84     case SUBMITTED:
85     case QUEUED:
86       infoPanel.setStatus(WebserviceInfo.STATE_QUEUING);
87       break;
88     case RUNNING:
89     case UNKNOWN: // unsure what to do with unknown
90       infoPanel.setStatus(WebserviceInfo.STATE_RUNNING);
91       break;
92     case COMPLETED:
93       infoPanel.setProgressBar(
94           MessageManager.getString("status.collecting_job_results"),
95           jobs[0].getInternalId());
96       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK);
97       break;
98     case FAILED:
99       infoPanel.removeProgressBar(jobs[0].getInternalId());
100       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
101       break;
102     case CANCELLED:
103       infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
104       break;
105     case SERVER_ERROR:
106       infoPanel.removeProgressBar(jobs[0].getInternalId());
107       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
108       break;
109     }
110   }
111
112   @Override
113   public void taskStarted(TaskI<AlignmentResult> source, List<? extends JobI> subJobs)
114   {
115     jobs = subJobs.toArray(new JobI[0]);
116     tabs = new int[subJobs.size()];
117     logOffset = new int[subJobs.size()];
118     errLogOffset = new int[subJobs.size()];
119     for (int i = 0; i < subJobs.size(); i++)
120     {
121       JobI job = jobs[i];
122       int tabIndex = infoPanel.addJobPane();
123       tabs[i] = tabIndex;
124       infoPanel.setProgressName(String.format("region %d", i), tabIndex);
125       infoPanel.setProgressText(tabIndex, alnTitle + "\nJob details\n");
126       // jobs should not have states other than invalid or ready at this point
127       if (job.getStatus() == JobStatus.INVALID)
128         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK);
129       else if (job.getStatus() == JobStatus.READY)
130         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING);
131     }
132   }
133
134   @Override
135   public void taskCompleted(TaskI<AlignmentResult> source, AlignmentResult result)
136   {
137     SwingUtilities.invokeLater(() -> infoPanel.removeProgressBar(jobs[0].getInternalId()));
138     if (result == null)
139     {
140       SwingUtilities.invokeLater(infoPanel::setFinishedNoResults);
141       return;
142     }
143     infoPanel.showResultsNewFrame.addActionListener(evt -> {
144       var aln = result.getAlignment();
145       // copy alignment for each frame to have its own isntance
146       var alnCpy = new Alignment(aln);
147       alnCpy.setGapCharacter(aln.getGapCharacter());
148       alnCpy.setDataset(aln.getDataset());
149       displayResultsNewFrame(alnCpy, result.getAlignmentOrders(),
150           result.getHiddenColumns());
151     });
152     SwingUtilities.invokeLater(infoPanel::setResultsReady);
153   }
154
155   private void displayResultsNewFrame(Alignment aln,
156       List<AlignmentOrder> alorders, HiddenColumns hidden)
157   {
158     AlignFrame newFrame = new AlignFrame(aln, hidden, AlignFrame.DEFAULT_WIDTH,
159         AlignFrame.DEFAULT_HEIGHT);
160     newFrame.getFeatureRenderer().transferSettings(
161         frame.getFeatureRenderer().getSettings());
162     if (alorders.size() > 0)
163     {
164       addSortByMenuItems(newFrame, alorders);
165     }
166
167     var requestingFrame = frame;
168     var splitContainer = requestingFrame.getSplitViewContainer();
169     if (splitContainer != null && splitContainer.getComplement(requestingFrame) != null)
170     {
171       AlignmentI complement = splitContainer.getComplement(requestingFrame);
172       String complementTitle = splitContainer.getComplementTitle(requestingFrame);
173       Alignment copyComplement = new Alignment(complement);
174       copyComplement.setGapCharacter(complement.getGapCharacter());
175       copyComplement.setDataset(complement.getDataset());
176       copyComplement.alignAs(aln);
177       if (copyComplement.getHeight() > 0)
178       {
179         newFrame.setTitle(alnTitle);
180         AlignFrame newFrame2 = new AlignFrame(copyComplement,
181             AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
182         newFrame2.setTitle(complementTitle);
183         String linkedTitle = MessageManager.getString("label.linked_view_title");
184         JInternalFrame splitFrame = new SplitFrame(
185             aln.isNucleotide() ? newFrame : newFrame2,
186             aln.isNucleotide() ? newFrame2 : newFrame);
187         Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
188         return;
189       }
190     }
191     // no split frame or failed to create complementary alignment
192     Desktop.addInternalFrame(newFrame, alnTitle, AlignFrame.DEFAULT_WIDTH,
193         AlignFrame.DEFAULT_HEIGHT);
194   }
195   
196   private void addSortByMenuItems(AlignFrame frame, List<AlignmentOrder> alorders)
197   {
198     if (alorders.size() == 1)
199     {
200       frame.addSortByOrderMenuItem(service.getName() + " Ordering",
201           alorders.get(0));
202       return;
203     }
204     BitSet collected = new BitSet(alorders.size());
205     for (int i = 0, N = alorders.size(); i < N; i++)
206     {
207       if (collected.get(i))
208         continue;
209       var regions = new ArrayList<String>();
210       var order = alorders.get(i);
211       for (int j = i; j < N; j++)
212       {
213         if (!collected.get(j) && alorders.get(j).equals(order))
214         {
215           regions.add(Integer.toString(j + 1));
216           collected.set(j);
217         }
218       }
219       var orderName = String.format("%s Region %s Ordering",
220           service.getName(), String.join(",", regions));
221       frame.addSortByOrderMenuItem(orderName, order);
222     }
223   }
224
225   @Override
226   public void taskException(TaskI<AlignmentResult> source, Exception e)
227   {
228     Console.error(String.format("Service %s raised an exception.", service.getName()), e);
229     infoPanel.appendProgressText(e.getMessage());
230   }
231
232   @Override
233   public void subJobStatusChanged(TaskI<AlignmentResult> source, JobI job, JobStatus status)
234   {
235     int i = ArrayUtils.indexOf(jobs, job);
236     assert i >= 0 : "job does not exist";
237     if (i < 0)
238       // safeguard that should not happen irl
239       return;
240     int wsStatus;
241     switch (status)
242     {
243     case INVALID:
244     case COMPLETED:
245       wsStatus = WebserviceInfo.STATE_STOPPED_OK;
246       break;
247     case READY:
248     case SUBMITTED:
249     case QUEUED:
250       wsStatus = WebserviceInfo.STATE_QUEUING;
251       break;
252     case RUNNING:
253     case UNKNOWN:
254       wsStatus = WebserviceInfo.STATE_RUNNING;
255       break;
256     case FAILED:
257       wsStatus = WebserviceInfo.STATE_STOPPED_ERROR;
258       break;
259     case CANCELLED:
260       wsStatus = WebserviceInfo.STATE_CANCELLED_OK;
261       break;
262     case SERVER_ERROR:
263       wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR;
264       break;
265     default:
266       throw new AssertionError("Non-exhaustive switch statement");
267     }
268     infoPanel.setStatus(tabs[i], wsStatus);
269   }
270
271   @Override
272   public void subJobLogChanged(TaskI<AlignmentResult> source, JobI job, String log)
273   {
274     int i = ArrayUtils.indexOf(jobs, job);
275     assert i >= 0 : "job does not exist";
276     if (i < 0)
277       // safeguard that should never happen
278       return;
279     infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i]));
280   }
281
282   @Override
283   public void subJobErrorLogChanged(TaskI<AlignmentResult> source, JobI job, String log)
284   {
285     int i = ArrayUtils.indexOf(jobs, job);
286     assert i >= 0 : "job does not exist";
287     if (i < 0)
288       // safeguard that should never happen
289       return;
290     infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i]));
291   }
292
293 }