package jalview.ws2.gui; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import javax.swing.JInternalFrame; import javax.swing.SwingUtilities; import jalview.bin.Cache; import jalview.bin.Console; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.HiddenColumns; import jalview.gui.AlignFrame; import jalview.gui.Desktop; import jalview.gui.JvOptionPane; import jalview.gui.SplitFrame; import jalview.gui.WebserviceInfo; import jalview.util.ArrayUtils; import jalview.util.MessageManager; import jalview.util.Pair; import jalview.ws2.actions.alignment.AlignmentAction; import jalview.ws2.actions.alignment.AlignmentResult; import jalview.ws2.actions.api.JobI; import jalview.ws2.actions.api.TaskEventListener; import jalview.ws2.actions.api.TaskI; import jalview.ws2.api.JobStatus; import jalview.ws2.api.WebService; import jalview.ws2.helpers.WSClientTaskWrapper; class AlignmentServiceGuiHandler implements TaskEventListener { private final WebService service; private final AlignFrame frame; private WebserviceInfo infoPanel; private String alnTitle; // title of the alignment used in new window private JobI[] jobs = new JobI[0]; private int[] tabs = new int[0]; private int[] logOffset = new int[0]; private int[] errLogOffset = new int[0]; public AlignmentServiceGuiHandler(AlignmentAction action, AlignFrame frame) { this.service = action.getWebService(); this.frame = frame; String panelInfo = String.format("%s using service hosted at %s%n%s", service.getName(), service.getUrl(), service.getDescription()); infoPanel = new WebserviceInfo(service.getName(), panelInfo, false); String actionName = action.getName() != null ? action.getName() : "Alignment"; alnTitle = String.format("%s %s of %s", service.getName(), actionName, frame.getTitle()); } @Override public void taskStatusChanged(TaskI source, JobStatus status) { switch (status) { case INVALID: infoPanel.setVisible(false); JvOptionPane.showMessageDialog(frame, MessageManager.getString("info.invalid_msa_input_mininfo"), MessageManager.getString("info.invalid_msa_notenough"), JvOptionPane.INFORMATION_MESSAGE); break; case READY: infoPanel.setthisService(new WSClientTaskWrapper(source)); infoPanel.setVisible(true); // intentional no break case SUBMITTED: case QUEUED: infoPanel.setStatus(WebserviceInfo.STATE_QUEUING); break; case RUNNING: case UNKNOWN: // unsure what to do with unknown infoPanel.setStatus(WebserviceInfo.STATE_RUNNING); break; case COMPLETED: infoPanel.setProgressBar( MessageManager.getString("status.collecting_job_results"), jobs[0].getInternalId()); infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK); break; case FAILED: infoPanel.removeProgressBar(jobs[0].getInternalId()); infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); break; case CANCELLED: infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK); break; case SERVER_ERROR: infoPanel.removeProgressBar(jobs[0].getInternalId()); infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); break; } } @Override public void taskStarted(TaskI source, List subJobs) { jobs = subJobs.toArray(new JobI[0]); tabs = new int[subJobs.size()]; logOffset = new int[subJobs.size()]; errLogOffset = new int[subJobs.size()]; for (int i = 0; i < subJobs.size(); i++) { JobI job = jobs[i]; int tabIndex = infoPanel.addJobPane(); tabs[i] = tabIndex; infoPanel.setProgressName(String.format("region %d", i), tabIndex); infoPanel.setProgressText(tabIndex, alnTitle + "\nJob details\n"); // jobs should not have states other than invalid or ready at this point if (job.getStatus() == JobStatus.INVALID) infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK); else if (job.getStatus() == JobStatus.READY) infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING); } } @Override public void taskCompleted(TaskI source, AlignmentResult result) { SwingUtilities.invokeLater(() -> infoPanel.removeProgressBar(jobs[0].getInternalId())); if (result == null) { SwingUtilities.invokeLater(infoPanel::setFinishedNoResults); return; } infoPanel.showResultsNewFrame.addActionListener(evt -> { var aln = result.getAlignment(); // copy alignment for each frame to have its own isntance var alnCpy = new Alignment(aln); alnCpy.setGapCharacter(aln.getGapCharacter()); alnCpy.setDataset(aln.getDataset()); displayResultsNewFrame(alnCpy, result.getAlignmentOrders(), result.getHiddenColumns()); }); SwingUtilities.invokeLater(infoPanel::setResultsReady); } private void displayResultsNewFrame(Alignment aln, List alorders, HiddenColumns hidden) { AlignFrame newFrame = new AlignFrame(aln, hidden, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); newFrame.getFeatureRenderer().transferSettings( frame.getFeatureRenderer().getSettings()); if (alorders.size() > 0) { addSortByMenuItems(newFrame, alorders); } var requestingFrame = frame; var splitContainer = requestingFrame.getSplitViewContainer(); if (splitContainer != null && splitContainer.getComplement(requestingFrame) != null) { AlignmentI complement = splitContainer.getComplement(requestingFrame); String complementTitle = splitContainer.getComplementTitle(requestingFrame); Alignment copyComplement = new Alignment(complement); copyComplement.setGapCharacter(complement.getGapCharacter()); copyComplement.setDataset(complement.getDataset()); copyComplement.alignAs(aln); if (copyComplement.getHeight() > 0) { newFrame.setTitle(alnTitle); AlignFrame newFrame2 = new AlignFrame(copyComplement, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); newFrame2.setTitle(complementTitle); String linkedTitle = MessageManager.getString("label.linked_view_title"); JInternalFrame splitFrame = new SplitFrame( aln.isNucleotide() ? newFrame : newFrame2, aln.isNucleotide() ? newFrame2 : newFrame); Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1); return; } } // no split frame or failed to create complementary alignment Desktop.addInternalFrame(newFrame, alnTitle, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } private void addSortByMenuItems(AlignFrame frame, List alorders) { if (alorders.size() == 1) { frame.addSortByOrderMenuItem(service.getName() + " Ordering", alorders.get(0)); return; } BitSet collected = new BitSet(alorders.size()); for (int i = 0, N = alorders.size(); i < N; i++) { if (collected.get(i)) continue; var regions = new ArrayList(); var order = alorders.get(i); for (int j = i; j < N; j++) { if (!collected.get(j) && alorders.get(j).equals(order)) { regions.add(Integer.toString(j + 1)); collected.set(j); } } var orderName = String.format("%s Region %s Ordering", service.getName(), String.join(",", regions)); frame.addSortByOrderMenuItem(orderName, order); } } @Override public void taskException(TaskI source, Exception e) { Console.error(String.format("Service %s raised an exception.", service.getName()), e); infoPanel.appendProgressText(e.getMessage()); } @Override public void subJobStatusChanged(TaskI source, JobI job, JobStatus status) { int i = ArrayUtils.indexOf(jobs, job); assert i >= 0 : "job does not exist"; if (i < 0) // safeguard that should not happen irl return; int wsStatus; switch (status) { case INVALID: case COMPLETED: wsStatus = WebserviceInfo.STATE_STOPPED_OK; break; case READY: case SUBMITTED: case QUEUED: wsStatus = WebserviceInfo.STATE_QUEUING; break; case RUNNING: case UNKNOWN: wsStatus = WebserviceInfo.STATE_RUNNING; break; case FAILED: wsStatus = WebserviceInfo.STATE_STOPPED_ERROR; break; case CANCELLED: wsStatus = WebserviceInfo.STATE_CANCELLED_OK; break; case SERVER_ERROR: wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR; break; default: throw new AssertionError("Non-exhaustive switch statement"); } infoPanel.setStatus(tabs[i], wsStatus); } @Override public void subJobLogChanged(TaskI source, JobI job, String log) { int i = ArrayUtils.indexOf(jobs, job); assert i >= 0 : "job does not exist"; if (i < 0) // safeguard that should never happen return; infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i])); } @Override public void subJobErrorLogChanged(TaskI source, JobI job, String log) { int i = ArrayUtils.indexOf(jobs, job); assert i >= 0 : "job does not exist"; if (i < 0) // safeguard that should never happen return; infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i])); } }