Merge branch 'jpred-fix' into bug/JAL-3807_jpred-with-slivka bug/JAL-3807_jpred-with-slivka
authorMateusz Warowny <mmzwarowny@dundee.ac.uk>
Fri, 9 Jul 2021 14:57:28 +0000 (16:57 +0200)
committerMateusz Warowny <mmzwarowny@dundee.ac.uk>
Fri, 9 Jul 2021 14:57:28 +0000 (16:57 +0200)
src/jalview/ws/JPredThread.java [deleted file]
src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java [deleted file]
src/jalview/ws/api/JPredServiceI.java [new file with mode: 0644]
src/jalview/ws/api/ServiceWithParameters.java
src/jalview/ws/jws2/JPredClient.java [moved from src/jalview/ws/JPredClient.java with 68% similarity]
src/jalview/ws/jws2/JPredThread.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaJPredServiceInstance.java
src/jalview/ws/slivkaws/SlivkaMsaServiceInstance.java

diff --git a/src/jalview/ws/JPredThread.java b/src/jalview/ws/JPredThread.java
deleted file mode 100644 (file)
index ed6d4af..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-package jalview.ws;
-
-import static java.lang.String.format;
-
-import java.util.Hashtable;
-import java.util.List;
-
-import jalview.bin.Cache;
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AlignmentView;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.WebserviceInfo;
-import jalview.util.MessageManager;
-import jalview.ws.api.CancellableI;
-import jalview.ws.api.JPredMutlipleAlignmentServiceI;
-import jalview.ws.gui.WsJob;
-import jalview.ws.gui.WsJob.JobState;
-
-
-class JPredJob extends WsJob
-{
-  Hashtable<?, ?> sequenceInfo;
-  List<SequenceI> msf;
-  int[] delMap;
-  public AlignmentI alignment;
-  
-  public JPredJob(Hashtable<?, ?> sequenceInfo, SequenceI[] msf, int[] delMap)
-  {
-    this.sequenceInfo = sequenceInfo;
-    this.msf = List.of(msf);
-    this.delMap = delMap;
-  }
-  
-  @Override
-  public boolean hasValidInput()
-  {
-    return true;
-  }
-}
-
-
-public class JPredThread extends AWSThread implements WSClientI
-{
-  
-  private JPredMutlipleAlignmentServiceI server;
-  private String title;
-  private Hashtable<?, ?> sequenceInfo;
-  private SequenceI[] msf;
-  private int[] delMap;  
-  
-  public JPredThread(WebserviceInfo wsInfo, String title,
-      JPredMutlipleAlignmentServiceI server, Hashtable<?, ?> sequenceInfo,
-      SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame,
-      String wsURL)
-  {
-    super(frame, wsInfo, view, wsURL);
-    this.server = server;
-    this.title = title;
-    this.sequenceInfo = sequenceInfo;
-    this.msf = msf;
-    this.delMap = delMap;
-    JPredJob job = new JPredJob(sequenceInfo, msf, delMap);
-    this.jobs = new JPredJob[] { job };
-  }
-
-  @Override
-  public boolean isCancellable()
-  {
-    return server instanceof CancellableI;
-  }
-
-  @Override
-  public boolean canMergeResults()
-  {
-    return false;
-  }
-
-  @Override
-  public void cancelJob()
-  {
-    // TODO Auto-generated method stub
-    
-  }
-
-  @Override
-  public void pollJob(AWsJob job_) throws Exception
-  {
-    var job = (JPredJob) job_;
-    server.updateStatus(job);
-    server.updateJobProgress(job);
-  }
-
-  @Override
-  public void StartJob(AWsJob job_)
-  {
-    if (!(job_ instanceof JPredJob))
-      throw new RuntimeException("Invalid job type");
-    var job = (JPredJob) job_;
-    if (job.isSubmitted())
-    {
-      return;
-    }
-    try {
-      try
-      {
-        var jobHandle = server.align(job.msf);
-        if (jobHandle != null)
-          job.setJobHandle(jobHandle);
-      } 
-      catch (Throwable th) {
-        if (!server.handleSubmitError(th, job, wsInfo)) {
-          throw th;
-        }
-      }
-      if (job.getJobId() != null) {
-        job.setSubmitted(true);
-        job.setSubjobComplete(false);
-        return;
-      }
-      else {
-        throw new Exception(MessageManager.formatMessage(
-                "exception.web_service_returned_null_try_later",
-                new String[]
-                { WsUrl }));
-      }
-    }
-    catch (Throwable th)
-    {
-      // For unexpected errors
-      System.err.println(WebServiceName
-              + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
-              + "When contacting Server:" + WsUrl + "\n");
-      th.printStackTrace(System.err);
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      wsInfo.setStatus(job.getJobnum(),
-              WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      
-    }
-    finally
-    {
-      if (!job.isSubmitted())
-      {
-        job.setAllowedServerExceptions(0);
-        wsInfo.appendProgressText(job.getJobnum(), MessageManager.getString(
-                "info.failed_to_submit_sequences_for_alignment"));
-      }
-    }
-  }
-
-  @Override
-  public void parseResult()
-  {
-    long progbar = (long) (Math.random() * ~(1L << 63));
-    wsInfo.setProgressBar(
-        MessageManager.getString("status.collecting_job_results"), progbar);
-    int results = 0;
-    var finalState = new JobStateSummary();
-    try
-    {
-      for (int i = 0; i < jobs.length; i++) {
-        final var job = (JPredJob) jobs[i];
-        finalState.updateJobPanelState(wsInfo, OutputHeader, job);
-        if (job.isFinished()) {
-          try {
-            server.updateJobProgress(job);
-          }
-          catch (Exception e) {
-            Cache.log.warn(format(
-                "Exception when retrieving remaining Job progress data " +
-                "for job %s on server %s", job.getJobId(), WsUrl));
-            e.printStackTrace();
-          }
-          // removed the waiting loop
-          Cache.log.debug(format("Job Execution file for job: %s " +
-              "on server %s%n%s", job.getJobId(), WsUrl, job.getStatus()));
-          try {
-            job.alignment = server.getResult(job.getJobHandle());
-          }
-          catch (Exception e) {
-            if (!server.handleCollectionException(e, job, wsInfo)) {
-              Cache.log.error("Could not get alignment for job.", e);
-              job.setState(JobState.SERVERERROR);
-            }
-          }
-        }
-        finalState.updateJobPanelState(wsInfo, OutputHeader, job);
-        if (job.isSubmitted() && job.isSubjobComplete() && job.hasResults()) {
-          results++;
-        }
-      }
-    }
-    catch (Exception e) {
-      Cache.log.error(
-          "Unexpected exception when processing results for " + title, e);
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-    }
-    if (results > 0) {
-      wsInfo.showResultsNewFrame.addActionListener(
-          (evt) -> displayResults(true));
-      wsInfo.mergeResults.addActionListener(
-          (evt) -> displayResults(false));
-      wsInfo.setResultsReady();
-    }
-    else {
-      wsInfo.setFinishedNoResults();
-    }
-    updateGlobalStatus(finalState);
-    wsInfo.removeProgressBar(progbar);
-  }
-
-  
-
-  private void displayResults(boolean newWindow)
-  {
-    System.out.println("DISPLAYING THE RESULT");
-  }
-  
-}
diff --git a/src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java b/src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java
deleted file mode 100644 (file)
index 261f1f8..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package jalview.ws.api;
-
-import java.util.List;
-
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-
-public interface JPredMutlipleAlignmentServiceI extends JalviewWebServiceI
-{
-  public JobId align(List<SequenceI> sequences) throws Throwable;
-  
-  public AlignmentI getResult(JobId jobId) throws Exception;
-}
diff --git a/src/jalview/ws/api/JPredServiceI.java b/src/jalview/ws/api/JPredServiceI.java
new file mode 100644 (file)
index 0000000..5ef159d
--- /dev/null
@@ -0,0 +1,16 @@
+package jalview.ws.api;
+
+import java.util.List;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.JPredFile;
+
+public interface JPredServiceI extends JalviewWebServiceI
+{
+  public JobId predict(List<SequenceI> sequences, boolean msa) throws Throwable;
+
+  public AlignmentI getAlignment(JobId jobId) throws Exception;
+
+  public JPredFile getPrediction(JobId jobId) throws Exception;
+}
index d875fd7..4239afb 100644 (file)
@@ -2,7 +2,7 @@ package jalview.ws.api;
 
 import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
-import jalview.ws.JPredClient;
+import jalview.ws.jws2.JPredClient;
 import jalview.ws.jws2.MsaWSClient;
 import jalview.ws.jws2.SequenceAnnotationWSClient;
 import jalview.ws.params.ParamManager;
similarity index 68%
rename from src/jalview/ws/JPredClient.java
rename to src/jalview/ws/jws2/JPredClient.java
index f431a0c..1413dfe 100644 (file)
@@ -1,4 +1,4 @@
-package jalview.ws;
+package jalview.ws.jws2;
 
 import javax.swing.JMenuItem;
 
@@ -7,17 +7,19 @@ import jalview.bin.Cache;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.ws.api.JPredMutlipleAlignmentServiceI;
+import jalview.ws.WSClient;
+import jalview.ws.WSMenuEntryProviderI;
+import jalview.ws.api.JPredServiceI;
 import jalview.ws.api.ServiceWithParameters;
 
 public class JPredClient extends WSClient
 {
-  JPredMutlipleAlignmentServiceI server;
+  JPredServiceI server;
 
   public JPredClient(ServiceWithParameters sh, String title,
       AlignmentView alView, AlignFrame alFrame, boolean viewOnly)
   {
-    server = (JPredMutlipleAlignmentServiceI) sh.getEndpoint();
+    server = (JPredServiceI) sh.getEndpoint();
     wsInfo = setWebService(sh, false);
     startClient(title, alView, alFrame, viewOnly);
   }
@@ -27,16 +29,23 @@ public class JPredClient extends WSClient
   {
     var msf = view.getSequences();
     var seq = msf[0].getSeq('-');
-    if (msf.length <= 1)
-      throw new RuntimeException("You need more than one sequence.");
+    int[] delMap = null;
+    if (viewOnly)
+      delMap = view.getVisibleContigMapFor(seq.gapMap());
     var aln = new SequenceI[msf.length];
     for (int i = 0; i < msf.length; i++)
     {
       aln[i] = msf[i].getSeq('-');
     }
-    int[] delMap = viewOnly ?
-      view.getVisibleContigMapFor(seq.gapMap()) : null;
-    var sequenceInfo = SeqsetUtils.uniquify(aln, true);
+    var sequenceInfo = msf.length > 1 ? SeqsetUtils.uniquify(aln, true)
+            : SeqsetUtils.SeqCharacterHash(seq);
+    if (viewOnly)
+    {
+      String seqs[] = view.getSequenceStrings('-');
+      for (int i = 0; i < msf.length; i++)
+        aln[i].setSequence(seqs[i]);
+      seq.setSequence(seqs[0]);
+    }
     var thread = new JPredThread(wsInfo, title, server, sequenceInfo, aln,
         delMap, view, frame, WsURL);
     wsInfo.setthisService(thread);
@@ -52,14 +61,7 @@ public class JPredClient extends WSClient
       mi.setToolTipText(sh.getHostURL());
       mi.addActionListener((event) -> {
         var view = frame.gatherSeqOrMsaForSecStrPrediction();
-        if (view.getSequences().length > 1)
-        {
-          new JPredClient(sh, frame.getTitle(), view, frame, true);
-        }
-        else
-        {
-          Cache.log.error("Single sequence not supported");
-        }
+        new JPredClient(sh, frame.getTitle(), view, frame, true);
       });
       menu.add(mi);
     };
diff --git a/src/jalview/ws/jws2/JPredThread.java b/src/jalview/ws/jws2/JPredThread.java
new file mode 100644 (file)
index 0000000..d10ca8a
--- /dev/null
@@ -0,0 +1,406 @@
+package jalview.ws.jws2;
+
+import static java.lang.String.format;
+
+import java.util.Hashtable;
+import java.util.List;
+
+import jalview.analysis.SeqsetUtils;
+import jalview.bin.Cache;
+import jalview.commands.RemoveGapsCommand;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.WebserviceInfo;
+import jalview.io.JPredFile;
+import jalview.io.JnetAnnotationMaker;
+import jalview.util.MessageManager;
+import jalview.ws.AWSThread;
+import jalview.ws.AWsJob;
+import jalview.ws.JobStateSummary;
+import jalview.ws.WSClientI;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JPredServiceI;
+import jalview.ws.gui.WsJob;
+import jalview.ws.gui.WsJob.JobState;
+
+
+public class JPredThread extends AWSThread implements WSClientI
+{
+
+  private static class JPredJob extends WsJob
+  {
+    private final Hashtable<?, ?> sequenceInfo;
+    private final List<SequenceI> msf;
+    private final int[] delMap;
+    private AlignmentI alignment = null;
+    private HiddenColumns hiddenCols = null;
+
+    private JPredJob(Hashtable<?, ?> sequenceInfo, SequenceI[] msf, int[] delMap)
+    {
+      this.sequenceInfo = sequenceInfo;
+      this.msf = List.of(msf);
+      this.delMap = delMap;
+    }
+
+    @Override
+    public boolean hasValidInput()
+    {
+      return true;
+    }
+
+    @Override
+    public boolean hasResults()
+    {
+      return (isSubjobComplete() && alignment != null);
+    }
+
+    public boolean isMSA()
+    {
+      return msf.size() > 1;
+    }
+  }
+
+
+  private JPredServiceI server;
+  private String title;
+  private Hashtable<?, ?> sequenceInfo;
+  private SequenceI[] msf;
+  private int[] delMap;
+
+  public JPredThread(WebserviceInfo wsInfo, String title,
+      JPredServiceI server, Hashtable<?, ?> sequenceInfo,
+      SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame,
+      String wsURL)
+  {
+    super(frame, wsInfo, view, wsURL);
+    this.server = server;
+    this.title = title;
+    this.sequenceInfo = sequenceInfo;
+    this.msf = msf;
+    this.delMap = delMap;
+    JPredJob job = new JPredJob(sequenceInfo, msf, delMap);
+    this.jobs = new JPredJob[] { job };
+  }
+
+  @Override
+  public boolean isCancellable()
+  {
+    return server instanceof CancellableI;
+  }
+
+  @Override
+  public boolean canMergeResults()
+  {
+    return false;
+  }
+
+  @Override
+  public void cancelJob()
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  @Override
+  public void pollJob(AWsJob job_) throws Exception
+  {
+    var job = (JPredJob) job_;
+    server.updateStatus(job);
+    server.updateJobProgress(job);
+  }
+
+  @Override
+  public void StartJob(AWsJob job_)
+  {
+    if (!(job_ instanceof JPredJob))
+      throw new RuntimeException("Invalid job type");
+    var job = (JPredJob) job_;
+    if (job.isSubmitted())
+    {
+      return;
+    }
+    try {
+      try
+      {
+        var jobHandle = server.predict(job.msf, job.isMSA());
+        if (jobHandle != null)
+          job.setJobHandle(jobHandle);
+      }
+      catch (Throwable th) {
+        if (!server.handleSubmitError(th, job, wsInfo)) {
+          throw th;
+        }
+      }
+      if (job.getJobId() != null) {
+        job.setSubmitted(true);
+        job.setSubjobComplete(false);
+        return;
+      }
+      else {
+        throw new Exception(MessageManager.formatMessage(
+                "exception.web_service_returned_null_try_later",
+                new String[]
+                { WsUrl }));
+      }
+    }
+    catch (Throwable th)
+    {
+      // For unexpected errors
+      System.err.println(WebServiceName
+              + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
+              + "When contacting Server:" + WsUrl + "\n");
+      th.printStackTrace(System.err);
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      wsInfo.setStatus(job.getJobnum(),
+              WebserviceInfo.STATE_STOPPED_SERVERERROR);
+
+    }
+    finally
+    {
+      if (!job.isSubmitted())
+      {
+        job.setAllowedServerExceptions(0);
+        wsInfo.appendProgressText(job.getJobnum(), MessageManager.getString(
+                "info.failed_to_submit_sequences_for_alignment"));
+      }
+    }
+  }
+
+  @Override
+  public void parseResult()
+  {
+    long progbar = (long) (Math.random() * ~(1L << 63));
+    wsInfo.setProgressBar(
+        MessageManager.getString("status.collecting_job_results"), progbar);
+    int results = 0;
+    var finalState = new JobStateSummary();
+    try
+    {
+      for (int i = 0; i < jobs.length; i++) {
+        final var job = (JPredJob) jobs[i];
+        finalState.updateJobPanelState(wsInfo, OutputHeader, job);
+        if (job.isFinished()) {
+          try {
+            server.updateJobProgress(job);
+          }
+          catch (Exception e) {
+            Cache.log.warn(format(
+                "Exception when retrieving remaining Job progress data " +
+                "for job %s on server %s", job.getJobId(), WsUrl));
+            e.printStackTrace();
+          }
+          // removed the waiting loop
+          Cache.log.debug(format("Job Execution file for job: %s " +
+              "on server %s%n%s", job.getJobId(), WsUrl, job.getStatus()));
+          try {
+            prepareJobResult(job);
+          }
+          catch (Exception e) {
+            if (!server.handleCollectionException(e, job, wsInfo)) {
+              Cache.log.error("Could not get alignment for job.", e);
+              job.setState(JobState.SERVERERROR);
+            }
+          }
+        }
+        finalState.updateJobPanelState(wsInfo, OutputHeader, job);
+        if (job.isSubmitted() && job.isSubjobComplete() && job.hasResults()) {
+          results++;
+        }
+      }
+    }
+    catch (Exception e) {
+      Cache.log.error(
+          "Unexpected exception when processing results for " + title, e);
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+    }
+    if (results > 0) {
+      wsInfo.showResultsNewFrame.addActionListener(
+          (evt) -> displayResults(true));
+      wsInfo.mergeResults.addActionListener(
+          (evt) -> displayResults(false));
+      wsInfo.setResultsReady();
+    }
+    else {
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.appendInfoText("No jobs ran.");
+      wsInfo.setFinishedNoResults();
+    }
+    updateGlobalStatus(finalState);
+    wsInfo.removeProgressBar(progbar);
+  }
+
+  static final int msaIndex = 0;
+
+  private void prepareJobResult(JPredJob job) throws Exception
+  {
+    HiddenColumns hiddenCols = null;
+    int firstSeq = -1;
+    AlignmentI alignment;
+    var prediction = server.getPrediction(job.getJobHandle());
+    var preds = prediction.getSeqsAsArray();
+
+    if (job.msf.size() > 1)
+    {
+      if (job.delMap != null)
+      {
+        Object[] alandcolsel = input
+                .getAlignmentAndHiddenColumns(getGapChar());
+        alignment = new Alignment((SequenceI[]) alandcolsel[0]);
+        hiddenCols = (HiddenColumns) alandcolsel[1];
+      }
+      else
+      {
+        alignment = server.getAlignment(job.getJobHandle());
+        var seqs = new SequenceI[alignment.getHeight()];
+        for (int i = 0; i < alignment.getHeight(); i++)
+        {
+          seqs[i] = alignment.getSequenceAt(i);
+        }
+        if (!SeqsetUtils.deuniquify(sequenceInfo, seqs))
+        {
+          throw (new Exception(MessageManager.getString(
+                  "exception.couldnt_recover_sequence_properties_for_alignment")));
+        }
+      }
+      firstSeq = 0;
+      if (currentView.getDataset() != null)
+      {
+        alignment.setDataset(currentView.getDataset());
+      }
+      else
+      {
+        alignment.setDataset(null);
+      }
+      JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false,
+              job.delMap);
+    }
+    else
+    {
+      alignment = new Alignment(preds);
+      firstSeq = prediction.getQuerySeqPosition();
+      if (job.delMap != null)
+      {
+        Object[] alanndcolsel = input.getAlignmentAndHiddenColumns(getGapChar());
+        SequenceI[] seqs = (SequenceI[]) alanndcolsel[0];
+        new RemoveGapsCommand(MessageManager.getString("label.remove_gaps"),
+                new SequenceI[] {seqs[msaIndex]}, currentView);
+        SequenceI profileSeq = alignment.getSequenceAt(firstSeq);
+        profileSeq.setSequence(seqs[msaIndex].getSequenceAsString());
+      }
+      if (!SeqsetUtils.SeqCharacterUnhash(
+              alignment.getSequenceAt(firstSeq), sequenceInfo))
+      {
+        throw new Exception(MessageManager.getString(
+                "exception.couldnt_recover_sequence_props_for_jnet_query"));
+      }
+      alignment.setDataset(currentView.getDataset());
+      JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, true,
+              job.delMap);
+      SequenceI profileSeq = alignment.getSequenceAt(0);
+      alignToProfileSeq(alignment, profileSeq);
+      if (job.delMap != null)
+      {
+        hiddenCols = alignment.propagateInsertions(profileSeq, input);
+      }
+    }
+
+    for (var annot : alignment.getAlignmentAnnotation())
+    {
+      if (annot.sequenceRef != null)
+      {
+        replaceAnnotationOnAlignmentWith(annot, annot.label,
+                "jalview.ws.JPred", annot.sequenceRef);
+      }
+    }
+    job.alignment = alignment;
+    job.hiddenCols = hiddenCols;
+  }
+
+  private static void replaceAnnotationOnAlignmentWith(
+          AlignmentAnnotation newAnnot, String typeName, String calcId,
+          SequenceI aSeq)
+  {
+    SequenceI dsseq = aSeq.getDatasetSequence();
+    while (dsseq.getDatasetSequence() != null)
+    {
+      dsseq = dsseq.getDatasetSequence();
+    }
+    // look for same annotation on dataset and lift this one over
+    List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
+            typeName);
+    if (dsan != null && dsan.size() > 0)
+    {
+      for (AlignmentAnnotation dssan : dsan)
+      {
+        dsseq.removeAlignmentAnnotation(dssan);
+      }
+    }
+    AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
+    dsseq.addAlignmentAnnotation(dssan);
+    dssan.adjustForAlignment();
+  }
+
+  private static void alignToProfileSeq(AlignmentI al, SequenceI profileseq)
+  {
+    char gc = al.getGapCharacter();
+    int[] gapMap = profileseq.gapMap();
+    // insert gaps into profile
+    for (int lp = 0, r = 0; r < gapMap.length; r++)
+    {
+      if (gapMap[r] - lp > 1)
+      {
+        StringBuffer sb = new StringBuffer();
+        for (int s = 0, ns = gapMap[r] - lp; s < ns; s++)
+        {
+          sb.append(gc);
+        }
+        for (int s = 1, ns = al.getHeight(); s < ns; s++)
+        {
+          String sq = al.getSequenceAt(s).getSequenceAsString();
+          int diff = gapMap[r] - sq.length();
+          if (diff > 0)
+          {
+            // pad gaps
+            sq = sq + sb;
+            while ((diff = gapMap[r] - sq.length()) > 0)
+            {
+              sq = sq + ((diff >= sb.length()) ? sb.toString()
+                      : sb.substring(0, diff));
+            }
+            al.getSequenceAt(s).setSequence(sq);
+          }
+          else
+          {
+            al.getSequenceAt(s).setSequence(sq.substring(0, gapMap[r])
+                    + sb.toString() + sq.substring(gapMap[r]));
+          }
+        }
+      }
+      lp = gapMap[r];
+    }
+  }
+
+  private void displayResults(boolean newWindow)
+  {
+    if (jobs == null || jobs.length == 0)
+    {
+      return;
+    }
+    var job = (JPredJob) jobs[0];
+    if (job.hasResults() && newWindow)
+    {
+      job.alignment.setSeqrep(job.alignment.getSequenceAt(0));
+      AlignFrame frame = new AlignFrame(job.alignment, job.hiddenCols,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+    }
+  }
+
+}
index 00843fc..462be28 100644 (file)
@@ -6,16 +6,43 @@ import java.util.List;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
-import jalview.ws.api.JPredMutlipleAlignmentServiceI;
+import jalview.io.DataSourceType;
+import jalview.io.JPredFile;
+import jalview.ws.api.JPredServiceI;
 import jalview.ws.api.JobId;
+import jalview.ws.params.ArgumentI;
 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
 
 public class SlivkaJPredServiceInstance extends SlivkaWSInstance
-    implements JPredMutlipleAlignmentServiceI
+    implements JPredServiceI
 {
 
+  private class InputFormatParameter implements ArgumentI
+  {
+    String value = "";
+
+    @Override
+    public String getName()
+    {
+      return "format";
+    }
+
+    @Override
+    public String getValue()
+    {
+      return value;
+    }
+
+    @Override
+    public void setValue(String selectedItem)
+    {
+      value = selectedItem;
+    }
+  }
+
+
   public SlivkaJPredServiceInstance(SlivkaClient client,
       SlivkaService service, String action)
   {
@@ -24,28 +51,46 @@ public class SlivkaJPredServiceInstance extends SlivkaWSInstance
   }
 
   @Override
-  public JobId align(List<SequenceI> sequences) throws Throwable
+  public JobId predict(List<SequenceI> sequences, boolean msa) throws Throwable
   {
-    return super.submit(sequences, null, null);
+    // Hack allowing to send both single and msa jobs
+    // until msa and single sequence services are separated.
+    var arg = new InputFormatParameter();
+    arg.setValue(msa ? "fasta" : "seq");
+    return super.submit(sequences, null, List.of(arg));
   }
 
   @Override
-  public AlignmentI getResult(JobId jobId) throws Exception
+  public AlignmentI getAlignment(JobId jobId) throws Exception
   {
     List<RemoteFile> files;
     try {
       files = client.getJobResults(jobId.getJobId());
       for (RemoteFile f : files) {
-        if (f.getMimeType().equals("application/clustal")
-            || f.getMimeType().equals("application/fasta"))
+        var alignment = readAlignment(f);
+        if (alignment != null)
         {
-          return readAlignment(f);
+          return alignment;
         }
       }
     }
     catch (IOException e) {
       throw new IOError(e);
     }
-    return null;  
+    return null;
+  }
+
+  @Override
+  public JPredFile getPrediction(JobId jobId) throws Exception
+  {
+    List<RemoteFile> files = client.getJobResults(jobId.getJobId());
+    for (RemoteFile f : files)
+    {
+      if (f.getLabel().equals("concise"))
+      {
+        return new JPredFile(f.getURL(), DataSourceType.URL);
+      }
+    }
+    return null;
   }
 }
index 93c0bdb..8d6332c 100644 (file)
@@ -2,9 +2,6 @@ package jalview.ws.slivkaws;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
-import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
-import jalview.io.FormatAdapter;
 import jalview.ws.api.JobId;
 import jalview.ws.api.MultipleSequenceAlignmentI;
 import jalview.ws.params.ArgumentI;
@@ -16,7 +13,6 @@ import java.io.IOException;
 import java.rmi.ServerError;
 import java.util.List;
 
-import compbio.data.msa.Category;
 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
@@ -42,10 +38,10 @@ public class SlivkaMsaServiceInstance extends SlivkaWSInstance implements Multip
       List<RemoteFile> files = client.getJobResults(jobId.getJobId());
       for (RemoteFile f : files)
       {
-        if (f.getMimeType().equals("application/clustal") 
-            || f.getMimeType().equals("application/fasta"))
+        var aln = readAlignment(f);
+        if (aln != null)
         {
-          return readAlignment(f);
+          return aln;
         }
       }
     } catch (IOException e)