JAL-1601 Propagate hidden columns to the output alignment
[jalview.git] / src / jalview / ws2 / gui / SearchServiceGuiHandler.java
1 package jalview.ws2.gui;
2
3 import java.util.List;
4
5 import javax.swing.SwingUtilities;
6
7 import jalview.bin.Console;
8 import jalview.datamodel.Alignment;
9 import jalview.datamodel.AlignmentAnnotation;
10 import jalview.datamodel.AlignmentI;
11 import jalview.datamodel.HiddenColumns;
12 import jalview.gui.AlignFrame;
13 import jalview.gui.Desktop;
14 import jalview.gui.JvOptionPane;
15 import jalview.gui.WebserviceInfo;
16 import jalview.util.ArrayUtils;
17 import jalview.util.MessageManager;
18 import jalview.ws2.actions.api.ActionI;
19 import jalview.ws2.actions.api.JobI;
20 import jalview.ws2.actions.api.TaskEventListener;
21 import jalview.ws2.actions.api.TaskI;
22 import jalview.ws2.api.JobStatus;
23 import jalview.ws2.api.WebService;
24 import jalview.ws2.helpers.WSClientTaskWrapper;
25
26 import static java.lang.String.format;
27
28 class SearchServiceGuiHandler implements TaskEventListener<AlignmentI>
29 {
30   private final AlignFrame parentFrame;
31
32   private final ActionI<?> action;
33
34   private final WebService<?> service;
35
36   private WebserviceInfo infoPanel;
37
38   private JobI[] jobs = new JobI[0];
39
40   private int[] tabs = new int[0];
41
42   private int[] logOffset = new int[0];
43
44   private int[] errLogOffset = new int[0];
45
46   public SearchServiceGuiHandler(ActionI<?> action, AlignFrame parentFrame)
47   {
48     this.parentFrame = parentFrame;
49     this.action = action;
50     this.service = action.getWebService();
51     var info = String.format("%s search using service at %s%n%s",
52             service.getName(), service.getUrl(), service.getDescription());
53     this.infoPanel = new WebserviceInfo(service.getName(), info, false);
54   }
55
56   @Override
57   public void taskStarted(TaskI<AlignmentI> source,
58           List<? extends JobI> subJobs)
59   {
60     Console.debug(format("task %s#%x started with %d sub-jobs",
61             service.getName(), source.getUid(), subJobs.size()));
62     jobs = subJobs.toArray(new JobI[subJobs.size()]);
63     tabs = new int[subJobs.size()];
64     logOffset = new int[subJobs.size()];
65     errLogOffset = new int[subJobs.size()];
66     for (int i = 0; i < subJobs.size(); i++)
67     {
68       JobI job = jobs[i];
69       int tabIndex = infoPanel.addJobPane();
70       tabs[i] = tabIndex;
71       infoPanel.setProgressName(format("region %d", i), tabIndex);
72       infoPanel.setProgressText(tabIndex, "Job details:\n");
73       // jobs should not have states other than invalid or ready at this point
74       if (job.getStatus() == JobStatus.INVALID)
75         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK);
76       else if (job.getStatus() == JobStatus.READY)
77         infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING);
78     }
79   }
80
81   @Override
82   public void taskStatusChanged(TaskI<AlignmentI> source, JobStatus status)
83   {
84     Console.debug(format("task %s#%x status changed to %s",
85             service.getName(), source.getUid(), status));
86     switch (status)
87     {
88     case INVALID:
89       infoPanel.setVisible(false);
90       JvOptionPane.showMessageDialog(parentFrame,
91               MessageManager.getString("info.invalid_search_input"),
92               MessageManager.getString("info.invalid_search_input"),
93               JvOptionPane.INFORMATION_MESSAGE);
94       break;
95     case READY:
96       infoPanel.setthisService(new WSClientTaskWrapper(source));
97       infoPanel.setVisible(true);
98       // intentional no break
99     case SUBMITTED:
100     case QUEUED:
101       infoPanel.setStatus(WebserviceInfo.STATE_QUEUING);
102       break;
103     case RUNNING:
104     case UNKNOWN: // unsure what to do with unknown
105       infoPanel.setStatus(WebserviceInfo.STATE_RUNNING);
106       break;
107     case COMPLETED:
108       infoPanel.setProgressBar(
109               MessageManager.getString("status.collecting_job_results"),
110               jobs[0].getInternalId());
111       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK);
112       break;
113     case FAILED:
114       infoPanel.removeProgressBar(jobs[0].getInternalId());
115       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
116       break;
117     case CANCELLED:
118       infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
119       break;
120     case SERVER_ERROR:
121       infoPanel.removeProgressBar(jobs[0].getInternalId());
122       infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
123       break;
124     }
125   }
126
127   @Override
128   public void taskCompleted(TaskI<AlignmentI> source, AlignmentI result)
129   {
130     Console.debug(format("task %s#%x completed", service.getName(),
131             source.getUid()));
132     SwingUtilities.invokeLater(
133             () -> infoPanel.removeProgressBar(jobs[0].getInternalId()));
134     if (result == null)
135     {
136       SwingUtilities.invokeLater(infoPanel::setFinishedNoResults);
137       return;
138     }
139     infoPanel.showResultsNewFrame.addActionListener(evt -> {
140       // copy alignment for each frame to have its own instance
141       var alnCpy = new Alignment(result);
142       alnCpy.setGapCharacter(result.getGapCharacter());
143       alnCpy.setDataset(result.getDataset());
144       for (AlignmentAnnotation annotation : result.getAlignmentAnnotation())
145         alnCpy.addAnnotation(new AlignmentAnnotation(annotation));
146       if (result.hasSeqrep())
147       {
148         int idx = result.findIndex(result.getSeqrep());
149         if (idx >= 0)
150           alnCpy.setSeqrep(alnCpy.getSequenceAt(idx));
151       }
152       if (result.getHiddenColumns() != null)
153         alnCpy.setHiddenColumns(new HiddenColumns(result.getHiddenColumns()));
154       displayResultsNewFrame(alnCpy);
155     });
156     SwingUtilities.invokeLater(infoPanel::setResultsReady);
157   }
158
159   private void displayResultsNewFrame(AlignmentI aln)
160   {
161     AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH,
162             AlignFrame.DEFAULT_HEIGHT);
163     frame.getFeatureRenderer().transferSettings(
164             parentFrame.getFeatureRenderer().getSettings());
165     var actionName = action.getName() != null ? action.getName() : "Search";
166     var title = String.format("%s %s of %s", service.getName(), actionName,
167             parentFrame.getTitle());
168     Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
169             AlignFrame.DEFAULT_HEIGHT);
170   }
171
172   @Override
173   public void taskException(TaskI<AlignmentI> source, Exception e)
174   {
175     Console.error(format("Task %s#%x raised an exception.",
176             service.getName(), source.getUid()), e);
177     infoPanel.appendProgressText(e.getMessage());
178   }
179
180   @Override
181   public void subJobStatusChanged(TaskI<AlignmentI> source, JobI job,
182           JobStatus status)
183   {
184     Console.debug(format("sub-job %x status changed to %s",
185             job.getInternalId(), status));
186     int i = ArrayUtils.indexOf(jobs, job);
187     assert i >= 0 : "job does not exist";
188     if (i < 0)
189       // safeguard that should not happen irl
190       return;
191     int wsStatus;
192     switch (status)
193     {
194     case INVALID:
195     case COMPLETED:
196       wsStatus = WebserviceInfo.STATE_STOPPED_OK;
197       break;
198     case READY:
199     case SUBMITTED:
200     case QUEUED:
201       wsStatus = WebserviceInfo.STATE_QUEUING;
202       break;
203     case RUNNING:
204     case UNKNOWN:
205       wsStatus = WebserviceInfo.STATE_RUNNING;
206       break;
207     case FAILED:
208       wsStatus = WebserviceInfo.STATE_STOPPED_ERROR;
209       break;
210     case CANCELLED:
211       wsStatus = WebserviceInfo.STATE_CANCELLED_OK;
212       break;
213     case SERVER_ERROR:
214       wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR;
215       break;
216     default:
217       throw new AssertionError("Non-exhaustive switch statement");
218     }
219     infoPanel.setStatus(tabs[i], wsStatus);
220   }
221
222   @Override
223   public void subJobLogChanged(TaskI<AlignmentI> source, JobI job,
224           String log)
225   {
226     int i = ArrayUtils.indexOf(jobs, job);
227     assert i >= 0 : "job does not exist";
228     if (i < 0)
229       // safeguard that should never happen
230       return;
231     infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i]));
232   }
233
234   @Override
235   public void subJobErrorLogChanged(TaskI<AlignmentI> source, JobI job,
236           String log)
237   {
238     int i = ArrayUtils.indexOf(jobs, job);
239     assert i >= 0 : "job does not exist";
240     if (i < 0)
241       // safeguard that should never happen
242       return;
243     infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i]));
244   }
245 }