JAL-1601 Propagate hidden columns to the output alignment
[jalview.git] / src / jalview / ws2 / gui / SearchServiceGuiHandler.java
index b3ae5c1..e66315f 100644 (file)
@@ -2,94 +2,244 @@ package jalview.ws2.gui;
 
 import java.util.List;
 
+import javax.swing.SwingUtilities;
+
 import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.gui.WebserviceInfo;
+import jalview.util.ArrayUtils;
+import jalview.util.MessageManager;
+import jalview.ws2.actions.api.ActionI;
 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;
+
+import static java.lang.String.format;
 
-public class SearchServiceGuiHandler implements TaskEventListener<AlignmentI>
+class SearchServiceGuiHandler implements TaskEventListener<AlignmentI>
 {
   private final AlignFrame parentFrame;
-  
-  public SearchServiceGuiHandler(AlignFrame parentFrame)
+
+  private final ActionI<?> action;
+
+  private final WebService<?> service;
+
+  private WebserviceInfo infoPanel;
+
+  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 SearchServiceGuiHandler(ActionI<?> action, AlignFrame parentFrame)
   {
     this.parentFrame = parentFrame;
+    this.action = action;
+    this.service = action.getWebService();
+    var info = String.format("%s search using service at %s%n%s",
+            service.getName(), service.getUrl(), service.getDescription());
+    this.infoPanel = new WebserviceInfo(service.getName(), info, false);
   }
 
   @Override
   public void taskStarted(TaskI<AlignmentI> source,
           List<? extends JobI> subJobs)
   {
-    Console.info("task started with " + subJobs.size() + " jobs");
-    // TODO Auto-generated method stub
-    
+    Console.debug(format("task %s#%x started with %d sub-jobs",
+            service.getName(), source.getUid(), subJobs.size()));
+    jobs = subJobs.toArray(new JobI[subJobs.size()]);
+    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(format("region %d", i), tabIndex);
+      infoPanel.setProgressText(tabIndex, "Job 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 taskStatusChanged(TaskI<AlignmentI> source, JobStatus status)
   {
-    Console.info("task status " + status);
-    // TODO Auto-generated method stub
-    
+    Console.debug(format("task %s#%x status changed to %s",
+            service.getName(), source.getUid(), status));
+    switch (status)
+    {
+    case INVALID:
+      infoPanel.setVisible(false);
+      JvOptionPane.showMessageDialog(parentFrame,
+              MessageManager.getString("info.invalid_search_input"),
+              MessageManager.getString("info.invalid_search_input"),
+              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 taskCompleted(TaskI<AlignmentI> source, AlignmentI result)
   {
-    Console.info("task completed");
-    displayResultsNewFrame(result);
+    Console.debug(format("task %s#%x completed", service.getName(),
+            source.getUid()));
+    SwingUtilities.invokeLater(
+            () -> infoPanel.removeProgressBar(jobs[0].getInternalId()));
+    if (result == null)
+    {
+      SwingUtilities.invokeLater(infoPanel::setFinishedNoResults);
+      return;
+    }
+    infoPanel.showResultsNewFrame.addActionListener(evt -> {
+      // copy alignment for each frame to have its own instance
+      var alnCpy = new Alignment(result);
+      alnCpy.setGapCharacter(result.getGapCharacter());
+      alnCpy.setDataset(result.getDataset());
+      for (AlignmentAnnotation annotation : result.getAlignmentAnnotation())
+        alnCpy.addAnnotation(new AlignmentAnnotation(annotation));
+      if (result.hasSeqrep())
+      {
+        int idx = result.findIndex(result.getSeqrep());
+        if (idx >= 0)
+          alnCpy.setSeqrep(alnCpy.getSequenceAt(idx));
+      }
+      if (result.getHiddenColumns() != null)
+        alnCpy.setHiddenColumns(new HiddenColumns(result.getHiddenColumns()));
+      displayResultsNewFrame(alnCpy);
+    });
+    SwingUtilities.invokeLater(infoPanel::setResultsReady);
   }
 
-  @Override
-  public void taskException(TaskI<AlignmentI> source, Exception e)
+  private void displayResultsNewFrame(AlignmentI aln)
   {
-    Console.info("task failed", e);
-    // TODO Auto-generated method stub
-    
+    AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    frame.getFeatureRenderer().transferSettings(
+            parentFrame.getFeatureRenderer().getSettings());
+    var actionName = action.getName() != null ? action.getName() : "Search";
+    var title = String.format("%s %s of %s", service.getName(), actionName,
+            parentFrame.getTitle());
+    Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
   }
 
   @Override
-  public void taskRestarted(TaskI<AlignmentI> source)
+  public void taskException(TaskI<AlignmentI> source, Exception e)
   {
-    Console.info("task restarted");
-    // TODO Auto-generated method stub
-    
+    Console.error(format("Task %s#%x raised an exception.",
+            service.getName(), source.getUid()), e);
+    infoPanel.appendProgressText(e.getMessage());
   }
 
   @Override
   public void subJobStatusChanged(TaskI<AlignmentI> source, JobI job,
           JobStatus status)
   {
-    Console.info("sub-job " + job.getInternalId() + " status " + status);
-    // TODO Auto-generated method stub
-    
+    Console.debug(format("sub-job %x status changed to %s",
+            job.getInternalId(), 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<AlignmentI> source, JobI job,
           String log)
   {
-    // TODO Auto-generated method stub
-    
+    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<AlignmentI> source, JobI job,
           String log)
   {
-    // TODO Auto-generated method stub
-    
-  }
-
-  private void displayResultsNewFrame(AlignmentI aln)
-  {
-    AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-    frame.getFeatureRenderer().transferSettings(
-            parentFrame.getFeatureRenderer().getSettings());
-    Desktop.addInternalFrame(frame, "title", AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+    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]));
   }
 }