1 package jalview.ws2.gui;
3 import static java.util.Objects.requireNonNullElse;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.BitSet;
8 import java.util.HashMap;
9 import java.util.LinkedHashMap;
10 import java.util.LinkedList;
11 import java.util.List;
13 import javax.swing.JInternalFrame;
14 import javax.swing.SwingUtilities;
16 import jalview.bin.Cache;
17 import jalview.bin.Console;
18 import jalview.datamodel.Alignment;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.AlignmentOrder;
21 import jalview.datamodel.HiddenColumns;
22 import jalview.gui.AlignFrame;
23 import jalview.gui.Desktop;
24 import jalview.gui.JvOptionPane;
25 import jalview.gui.SplitFrame;
26 import jalview.gui.WebserviceInfo;
27 import jalview.util.ArrayUtils;
28 import jalview.util.MessageManager;
29 import jalview.util.Pair;
30 import jalview.ws2.actions.alignment.AlignmentAction;
31 import jalview.ws2.actions.alignment.AlignmentResult;
32 import jalview.ws2.actions.api.JobI;
33 import jalview.ws2.actions.api.TaskEventListener;
34 import jalview.ws2.actions.api.TaskI;
35 import jalview.ws2.api.JobStatus;
36 import jalview.ws2.api.WebService;
37 import jalview.ws2.helpers.WSClientTaskWrapper;
39 class AlignmentServiceGuiHandler
40 implements TaskEventListener<AlignmentResult>
42 private final WebService<?> service;
44 private final AlignFrame frame;
46 private WebserviceInfo infoPanel;
48 private String alnTitle; // title of the alignment used in new window
50 private JobI[] jobs = new JobI[0];
52 private int[] tabs = new int[0];
54 private int[] logOffset = new int[0];
56 private int[] errLogOffset = new int[0];
58 public AlignmentServiceGuiHandler(AlignmentAction action, AlignFrame frame)
60 this.service = action.getWebService();
62 String panelInfo = String.format("%s using service hosted at %s%n%s",
63 service.getName(), service.getUrl(), service.getDescription());
64 infoPanel = new WebserviceInfo(service.getName(), panelInfo, false);
65 String actionName = requireNonNullElse(action.getName(), "Alignment");
66 alnTitle = String.format("%s %s of %s", service.getName(), actionName,
71 public void taskStatusChanged(TaskI<AlignmentResult> source, JobStatus status)
76 infoPanel.setVisible(false);
77 JvOptionPane.showMessageDialog(frame,
78 MessageManager.getString("info.invalid_msa_input_mininfo"),
79 MessageManager.getString("info.invalid_msa_notenough"),
80 JvOptionPane.INFORMATION_MESSAGE);
83 infoPanel.setthisService(new WSClientTaskWrapper(source));
84 infoPanel.setVisible(true);
85 // intentional no break
88 infoPanel.setStatus(WebserviceInfo.STATE_QUEUING);
91 case UNKNOWN: // unsure what to do with unknown
92 infoPanel.setStatus(WebserviceInfo.STATE_RUNNING);
95 infoPanel.setProgressBar(
96 MessageManager.getString("status.collecting_job_results"),
97 jobs[0].getInternalId());
98 infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK);
101 infoPanel.removeProgressBar(jobs[0].getInternalId());
102 infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
105 infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
108 infoPanel.removeProgressBar(jobs[0].getInternalId());
109 infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
115 public void taskStarted(TaskI<AlignmentResult> source, List<? extends JobI> subJobs)
117 jobs = subJobs.toArray(new JobI[0]);
118 tabs = new int[subJobs.size()];
119 logOffset = new int[subJobs.size()];
120 errLogOffset = new int[subJobs.size()];
121 for (int i = 0; i < subJobs.size(); i++)
124 int tabIndex = infoPanel.addJobPane();
126 infoPanel.setProgressName(String.format("region %d", i), tabIndex);
127 infoPanel.setProgressText(tabIndex, alnTitle + "\nJob details\n");
128 // jobs should not have states other than invalid or ready at this point
129 if (job.getStatus() == JobStatus.INVALID)
130 infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK);
131 else if (job.getStatus() == JobStatus.READY)
132 infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING);
137 public void taskCompleted(TaskI<AlignmentResult> source, AlignmentResult result)
139 SwingUtilities.invokeLater(() -> infoPanel.removeProgressBar(jobs[0].getInternalId()));
142 SwingUtilities.invokeLater(infoPanel::setFinishedNoResults);
145 infoPanel.showResultsNewFrame.addActionListener(evt -> {
146 var aln = result.getAlignment();
147 // copy alignment for each frame to have its own isntance
148 var alnCpy = new Alignment(aln);
149 alnCpy.setGapCharacter(aln.getGapCharacter());
150 alnCpy.setDataset(aln.getDataset());
151 displayResultsNewFrame(alnCpy, result.getAlignmentOrders(),
152 result.getHiddenColumns());
154 SwingUtilities.invokeLater(infoPanel::setResultsReady);
157 private void displayResultsNewFrame(Alignment aln,
158 List<AlignmentOrder> alorders, HiddenColumns hidden)
160 AlignFrame newFrame = new AlignFrame(aln, hidden, AlignFrame.DEFAULT_WIDTH,
161 AlignFrame.DEFAULT_HEIGHT);
162 newFrame.getFeatureRenderer().transferSettings(
163 frame.getFeatureRenderer().getSettings());
164 if (alorders.size() > 0)
166 addSortByMenuItems(newFrame, alorders);
169 var requestingFrame = frame;
170 var splitContainer = requestingFrame.getSplitViewContainer();
171 if (splitContainer != null && splitContainer.getComplement(requestingFrame) != null)
173 AlignmentI complement = splitContainer.getComplement(requestingFrame);
174 String complementTitle = splitContainer.getComplementTitle(requestingFrame);
175 Alignment copyComplement = new Alignment(complement);
176 copyComplement.setGapCharacter(complement.getGapCharacter());
177 copyComplement.setDataset(complement.getDataset());
178 copyComplement.alignAs(aln);
179 if (copyComplement.getHeight() > 0)
181 newFrame.setTitle(alnTitle);
182 AlignFrame newFrame2 = new AlignFrame(copyComplement,
183 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
184 newFrame2.setTitle(complementTitle);
185 String linkedTitle = MessageManager.getString("label.linked_view_title");
186 JInternalFrame splitFrame = new SplitFrame(
187 aln.isNucleotide() ? newFrame : newFrame2,
188 aln.isNucleotide() ? newFrame2 : newFrame);
189 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
193 // no split frame or failed to create complementary alignment
194 Desktop.addInternalFrame(newFrame, alnTitle, AlignFrame.DEFAULT_WIDTH,
195 AlignFrame.DEFAULT_HEIGHT);
198 private void addSortByMenuItems(AlignFrame frame, List<AlignmentOrder> alorders)
200 if (alorders.size() == 1)
202 frame.addSortByOrderMenuItem(service.getName() + " Ordering",
206 BitSet collected = new BitSet(alorders.size());
207 for (int i = 0, N = alorders.size(); i < N; i++)
209 if (collected.get(i))
211 var regions = new ArrayList<String>();
212 var order = alorders.get(i);
213 for (int j = i; j < N; j++)
215 if (!collected.get(j) && alorders.get(j).equals(order))
217 regions.add(Integer.toString(j + 1));
221 var orderName = String.format("%s Region %s Ordering",
222 service.getName(), String.join(",", regions));
223 frame.addSortByOrderMenuItem(orderName, order);
228 public void taskException(TaskI<AlignmentResult> source, Exception e)
230 Console.error(String.format("Service %s raised an exception.", service.getName()), e);
231 infoPanel.appendProgressText(e.getMessage());
235 public void taskRestarted(TaskI<AlignmentResult> source)
237 // alignment services are not restartable
241 public void subJobStatusChanged(TaskI<AlignmentResult> source, JobI job, JobStatus status)
243 int i = ArrayUtils.indexOf(jobs, job);
244 assert i >= 0 : "job does not exist";
246 // safeguard that should not happen irl
253 wsStatus = WebserviceInfo.STATE_STOPPED_OK;
258 wsStatus = WebserviceInfo.STATE_QUEUING;
262 wsStatus = WebserviceInfo.STATE_RUNNING;
265 wsStatus = WebserviceInfo.STATE_STOPPED_ERROR;
268 wsStatus = WebserviceInfo.STATE_CANCELLED_OK;
271 wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR;
274 throw new AssertionError("Non-exhaustive switch statement");
276 infoPanel.setStatus(tabs[i], wsStatus);
280 public void subJobLogChanged(TaskI<AlignmentResult> source, JobI job, String log)
282 int i = ArrayUtils.indexOf(jobs, job);
283 assert i >= 0 : "job does not exist";
285 // safeguard that should never happen
287 infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i]));
291 public void subJobErrorLogChanged(TaskI<AlignmentResult> source, JobI job, String log)
293 int i = ArrayUtils.indexOf(jobs, job);
294 assert i >= 0 : "job does not exist";
296 // safeguard that should never happen
298 infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i]));