JAL-3851 some changes. HighlightSequenceEndpoint and SelectSequenceEndpoint
authorBen Soares <b.soares@dundee.ac.uk>
Wed, 8 Sep 2021 17:05:52 +0000 (18:05 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Wed, 8 Sep 2021 17:05:52 +0000 (18:05 +0100)
src/jalview/gui/AlignFrame.java
src/jalview/gui/SequenceFetcher.java
src/jalview/rest/API.java
src/jalview/rest/AbstractEndpoint.java
src/jalview/rest/AbstractEndpointAsync.java
src/jalview/rest/FetchSequencesEndpoint.java
src/jalview/rest/HighlightSequenceEndpoint.java
src/jalview/rest/SelectSequencesEndpoint.java [new file with mode: 0644]

index 5370437..6cfe42e 100644 (file)
@@ -55,8 +55,10 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 import javax.swing.ButtonGroup;
@@ -5876,6 +5878,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     return lastFeatureSettingsBounds;
   }
+
+  /*
+   * Caching hashmaps for jalview.rest.API
+   */
+  private static Map<String, AlignFrame> alignFrameMap = null;
+
+  public static AlignFrame getAlignFrameFromRestId(String id)
+  {
+    if (id == null || alignFrameMap == null)
+      return null;
+    return alignFrameMap.get(id);
+  }
+
+  public void cacheAlignFrameFromRestId(String id)
+  {
+    if (id == null)
+      return;
+    if (alignFrameMap == null)
+      alignFrameMap = new HashMap<>();
+    alignFrameMap.put(id, this);
+  }
 }
 
 class PrintThread extends Thread
index b57fa46..1fc56d8 100755 (executable)
@@ -128,10 +128,18 @@ public class SequenceFetcher extends JPanel implements Runnable
    * @param guiIndic
    * @param selectedDb
    * @param queryString
+   * @param interactive
    */
   public SequenceFetcher(IProgressIndicator guiIndic,
           final String selectedDb, final String queryString)
   {
+    this(guiIndic, selectedDb, queryString, true);
+  }
+
+  public SequenceFetcher(IProgressIndicator guiIndic,
+          final String selectedDb, final String queryString,
+          boolean interactive)
+  {
     this.progressIndicator = guiIndic;
     getSequenceFetcherSingleton();
     this.guiWindow = progressIndicator;
@@ -141,7 +149,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       alignFrame = (AlignFrame) progressIndicator;
     }
 
-    jbInit(selectedDb);
+    jbInit(selectedDb, interactive);
     textArea.setText(queryString);
 
     frame = new JInternalFrame();
@@ -158,7 +166,7 @@ public class SequenceFetcher extends JPanel implements Runnable
                     .getString("label.additional_sequence_fetcher"));
   }
 
-  private void jbInit(String selectedDb)
+  private void jbInit(String selectedDb, boolean interactive)
   {
     this.setLayout(new BorderLayout());
 
@@ -300,9 +308,22 @@ public class SequenceFetcher extends JPanel implements Runnable
     jScrollPane1.getViewport().add(textArea);
     idsPanel.add(jScrollPane1, BorderLayout.CENTER);
 
+    // En/disable or show/hide interactive elements
+    database.setEnabled(interactive);
+    exampleAccession.setVisible(interactive);
+    replacePunctuation.setVisible(interactive);
+    okBtn.setVisible(interactive);
+    exampleBtn.setVisible(interactive);
+    closeBtn.setVisible(interactive);
+    backBtn.setVisible(interactive);
+    jLabel1.setVisible(interactive);
+    clear.setVisible(interactive);
+    textArea.setEnabled(interactive);
+
     this.add(actionPanel, BorderLayout.SOUTH);
     this.add(idsPanel, BorderLayout.CENTER);
     this.add(databasePanel, BorderLayout.NORTH);
+
   }
 
   /**
@@ -415,10 +436,11 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   public void ok_actionPerformed()
   {
-    ok_actionPerformed(false);
+    ok_actionPerformed(false, null);
   }
 
-  public CompletableFuture ok_actionPerformed(boolean returnFuture)
+  public CompletableFuture<Void> ok_actionPerformed(boolean returnFuture,
+          String id)
   {
     /*
      * tidy inputs and check there is something to search for
@@ -457,11 +479,18 @@ public class SequenceFetcher extends JPanel implements Runnable
     backBtn.setEnabled(false);
 
     CompletableFuture<Void> worker = CompletableFuture
-            .runAsync(new Thread(this));
+            .runAsync(() -> runAndCacheAlignFrame(returnFuture, id));
 
     return returnFuture ? worker : null;
   }
 
+  private void runAndCacheAlignFrame(boolean cacheAlignFrame, String id)
+  {
+    AlignFrame af = this.run(cacheAlignFrame);
+    if (cacheAlignFrame && id != null && af != null)
+      af.cacheAlignFrameFromRestId(id);
+  }
+
   private void resetDialog()
   {
     exampleBtn.setEnabled(true);
@@ -474,6 +503,11 @@ public class SequenceFetcher extends JPanel implements Runnable
   @Override
   public void run()
   {
+    run(false);
+  }
+
+  public AlignFrame run(boolean returnAlignFrame)
+  {
     boolean addToLast = false;
     List<String> aresultq = new ArrayList<>();
     List<String> presultTitle = new ArrayList<>();
@@ -611,9 +645,10 @@ public class SequenceFetcher extends JPanel implements Runnable
                             : MessageManager.getString("status.processing"),
                     Thread.currentThread().hashCode());
     // process results
+    AlignFrame af = null;
     while (presult.size() > 0)
     {
-      parseResult(presult.remove(0), presultTitle.remove(0), null,
+      af = parseResult(presult.remove(0), presultTitle.remove(0), null,
               preferredFeatureColours);
     }
     // only remove visual delay after we finished parsing.
@@ -641,6 +676,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       showErrorMessage(sb.toString());
     }
     resetDialog();
+    return returnAlignFrame ? af : null;
   }
 
   /**
@@ -806,20 +842,21 @@ public class SequenceFetcher extends JPanel implements Runnable
     return "Retrieved from " + database.getSelectedItem();
   }
 
-  AlignmentI parseResult(AlignmentI al, String title,
+  AlignFrame parseResult(AlignmentI al, String title,
           FileFormatI currentFileFormat,
           FeatureSettingsModelI preferredFeatureColours)
   {
 
+    AlignFrame af = alignFrame;
     if (al != null && al.getHeight() > 0)
     {
       if (title == null)
       {
         title = getDefaultRetrievalTitle();
       }
-      if (alignFrame == null)
+      if (af == null)
       {
-        AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+        af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
         if (currentFileFormat != null)
         {
@@ -862,10 +899,10 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
       else
       {
-        alignFrame.viewport.addAlignment(al, title);
+        af.viewport.addAlignment(al, title);
       }
     }
-    return al;
+    return af;
   }
 
   void showErrorMessage(final String error)
index 0bd3caa..7d87120 100644 (file)
@@ -48,6 +48,7 @@ public class API extends RestHandler
     addEndpoint(new FetchSequencesEndpoint(this));
     addEndpoint(new InputAlignmentEndpoint(this));
     addEndpoint(new HighlightSequenceEndpoint(this));
+    addEndpoint(new SelectSequencesEndpoint(this));
 
     setPath(MY_PATH);
     this.registerHandler();
index 2db0366..77c3bb8 100644 (file)
@@ -125,4 +125,32 @@ public abstract class AbstractEndpoint implements EndpointI
     return true;
   }
 
+  public int[][] parseIntRanges(String rangesString)
+  {
+    String[] rangeStrings = rangesString.split(",");
+    int[][] ranges = new int[2][rangeStrings.length];
+    for (int i = 0; i < rangeStrings.length; i++)
+    {
+      String range = rangeStrings[i];
+      try
+      {
+        int hyphenpos = range.indexOf('-');
+        if (hyphenpos < 0)
+        {
+          ranges[0][i] = Integer.parseInt(range);
+          ranges[1][i] = ranges[0][i];
+        }
+        else
+        {
+          ranges[0][i] = Integer.parseInt(range.substring(0, hyphenpos));
+          ranges[1][i] = Integer.parseInt(range.substring(hyphenpos + 1));
+        }
+      } catch (NumberFormatException nfe)
+      {
+        return null;
+      }
+    }
+    return ranges;
+  }
+
 }
\ No newline at end of file
index eb8280f..90122bf 100644 (file)
@@ -91,19 +91,24 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
 
     if (checkStatus(request, response, Status.STARTED))
     {
-      returnStatus(response);
+      String finishedString = null;
+      if (getStatus() == Status.FINISHED)
+      {
+        finishedString = finished(request, response);
+      }
+      returnStatus(request, response, finishedString);
       return;
     }
 
     if (getCompletableFuture() == null)
     {
-      final Map<String, String> finalMap = stringsPassedToProcessAsync;
+      final Map<String, String> finalStringMap = stringsPassedToProcessAsync;
       setCompletableFuture(CompletableFuture.runAsync(() -> {
         // subclass method
-        this.processAsync(request, response, finalMap);
+        this.processAsync(request, response, finalStringMap);
       }));
     }
-    finaliseCompletableFuture();
+    addWhenCompleteCompletableFuture();
 
     // subclass method
     finalise(request, response);
@@ -116,6 +121,12 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
   {
   }
 
+  protected String finished(HttpServletRequest request,
+          HttpServletResponse response)
+  {
+    return null;
+  }
+
   /*
    * Shared methods below here
    */
@@ -140,16 +151,23 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
     // don't change a job's status if it has finished or died
     if (getStatus() == Status.FINISHED || getStatus() == Status.ERROR)
       return;
-    getAPI().getStatusMap().put(id, status);
+    API.getStatusMap().put(id, status);
   }
 
   protected Status getStatus()
   {
-    return getAPI().getStatusMap().get(getId());
+    getAPI();
+    return API.getStatusMap().get(getId());
   }
 
   protected void returnStatus(HttpServletResponse response)
   {
+    returnStatus(null, response, null);
+  }
+
+  protected void returnStatus(HttpServletRequest request,
+          HttpServletResponse response, String message)
+  {
     String id = getId();
     try
     {
@@ -158,10 +176,12 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
       {
         writer.write("id=" + id + "\n");
       }
-      if (getAPI().getRequestMap().get(id) != null)
+      getAPI();
+      if (API.getRequestMap().get(id) != null)
       {
-        writer.write("request="
-                + getAPI().getRequestMap().get(id).toString() + "\n");
+        getAPI();
+        writer.write(
+                "request=" + API.getRequestMap().get(id).toString() + "\n");
       }
       if (getStatus() != null)
       {
@@ -171,6 +191,10 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
         }
         writer.write("status=" + getStatus().toString() + "\n");
       }
+      if (message != null)
+      {
+        writer.write(message);
+      }
     } catch (IOException e)
     {
       Cache.debug(e);
@@ -192,7 +216,7 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
     {
       if (set != null)
         changeStatus(set);
-      getAPI().getRequestMap().put(id, request.getRequestURI());
+      API.getRequestMap().put(id, request.getRequestURI());
       return false;
     }
     else
@@ -201,7 +225,7 @@ public abstract class AbstractEndpointAsync extends AbstractEndpoint
     }
   }
 
-  protected void finaliseCompletableFuture()
+  protected void addWhenCompleteCompletableFuture()
   {
     String id = getId();
     cf.whenComplete((Void, e) -> {
index 71734c6..ac01d8a 100644 (file)
@@ -1,13 +1,19 @@
 package jalview.rest;
 
+import java.util.List;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.SequenceFetcher;
+import jalview.structure.StructureSelectionManager;
 import jalview.util.DBRefUtils;
 
 public class FetchSequencesEndpoint extends AbstractEndpointAsync
@@ -46,8 +52,8 @@ public class FetchSequencesEndpoint extends AbstractEndpointAsync
 
     String db = DBRefUtils.getCanonicalName(dbName);
     Desktop desktop = Desktop.instance;
-    sf = new SequenceFetcher(desktop, db, dbId);
-    setCompletableFuture(sf.ok_actionPerformed(true));
+    sf = new SequenceFetcher(desktop, db, dbId, false);
+    setCompletableFuture(sf.ok_actionPerformed(true, getId()));
   }
 
   protected void processAsync(HttpServletRequest request,
@@ -61,4 +67,34 @@ public class FetchSequencesEndpoint extends AbstractEndpointAsync
   {
     sf.close_actionPerformed(null);
   }
+
+  protected String finished(HttpServletRequest request,
+          HttpServletResponse response)
+  {
+    AlignFrame af = AlignFrame.getAlignFrameFromRestId(getId());
+    if (af == null)
+    {
+      return null;
+    }
+    List<AlignmentViewPanel> aps = (List<AlignmentViewPanel>) af
+            .getAlignPanels();
+    StringBuilder sb = new StringBuilder();
+    for (AlignmentViewPanel ap : aps)
+    {
+      StructureSelectionManager ssm = ap.getStructureSelectionManager();
+      // ap.getAlignViewport().getSequenceSetId()
+      AlignmentI al = ap.getAlignment();
+      List<SequenceI> seqs = (List<SequenceI>) al.getSequences();
+      for (SequenceI seq : seqs)
+      {
+        if (sb.length() > 0)
+          sb.append(",");
+        sb.append(seq.getName());
+      }
+    }
+    sb.insert(0, "sequences=");
+    sb.append("\n");
+    return sb.toString();
+  }
+
 }
index 3f63ebb..5aa6943 100644 (file)
@@ -1,8 +1,20 @@
 package jalview.rest;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.structure.StructureSelectionManager;
+
 public class HighlightSequenceEndpoint extends AbstractEndpoint
 {
   public HighlightSequenceEndpoint(API api)
@@ -10,13 +22,13 @@ public class HighlightSequenceEndpoint extends AbstractEndpoint
     super(api, path, name, parameters, description);
   }
 
-  protected static final String path = "highlight";
+  protected static final String path = "highlightsequence";
 
-  private static final String name = "Highlight positions";
+  private static final String name = "Highlight sequence positions";
 
   private static final String parameters = "<sequence names>,<ranges>";
 
-  private static final String description = "Highlight the specified sequences with the specified range(s)";
+  private static final String description = "Highlight the specified sequences at the specified position";
 
   public void processEndpoint(HttpServletRequest request,
           HttpServletResponse response)
@@ -27,31 +39,73 @@ public class HighlightSequenceEndpoint extends AbstractEndpoint
     }
     String[] parameters = getEndpointPathParameters(request);
 
-    String rangesString = parameters[0];
-    String[] rangeStrings = rangesString.split(",");
-    int[][] ranges = new int[rangeStrings.length][2];
-    for (int i = 0; i < rangeStrings.length; i++)
+    String posString = parameters[1];
+    int pos = -1;
+    try
+    {
+      pos = Integer.parseInt(posString);
+    } catch (NumberFormatException e)
+    {
+      returnError(request, response,
+              "Could not parse postition integer " + posString);
+    }
+
+    String sequenceNames = parameters[0];
+    String fromIdString = request.getParameter("fromId");
+
+    Map<SequenceI, StructureSelectionManager> ssmMap = new HashMap<>();
+    AlignFrame[] alignFrames;
+    if (fromIdString != null)
     {
-      String range = rangeStrings[i];
-      try
+      AlignFrame af = AlignFrame.getAlignFrameFromRestId(fromIdString);
+      if (af == null)
       {
-        int hyphenpos = range.indexOf('-');
-        if (hyphenpos < 0)
-        {
-          ranges[i][0] = Integer.parseInt(range);
-          ranges[i][1] = ranges[i][0];
-        }
-        else
+        returnError(request, response,
+                "fromId value '" + fromIdString + "' results not found");
+        return;
+      }
+      alignFrames = new AlignFrame[] { af };
+    }
+    else
+    {
+      alignFrames = Desktop.getAlignFrames();
+    }
+    if (alignFrames == null)
+      return;
+    for (int i = 0; i < alignFrames.length; i++)
+    {
+      AlignFrame af = alignFrames[i];
+      List<AlignmentViewPanel> aps = (List<AlignmentViewPanel>) af
+              .getAlignPanels();
+      for (AlignmentViewPanel ap : aps)
+      {
+        StructureSelectionManager ssm = ap.getStructureSelectionManager();
+        // ap.getAlignViewport().getSequenceSetId()
+        AlignmentI al = ap.getAlignment();
+        List<SequenceI> seqs = (List<SequenceI>) al.getSequences();
+        for (SequenceI seq : seqs)
         {
-          ranges[i][0] = Integer.parseInt(range.substring(0, hyphenpos));
-          ranges[i][1] = Integer.parseInt(range.substring(hyphenpos));
+          Cache.info("REMOVEME sequence name=" + seq.getName());
+          if (sequenceNames.equals(seq.getName()))
+          {
+            Cache.info("REMOVEME MATCHED " + seq.getName());
+            ssmMap.put(seq, ssm);
+          }
         }
-      } catch (NumberFormatException nfe)
+      }
+    }
+    // highlight
+    for (SequenceI seq : ssmMap.keySet())
+    {
+      StructureSelectionManager ssm = ssmMap.get(seq);
+      if (ssm == null)
       {
-        returnError(request, response,
-                "couldn't parse ranges component '" + range + "'");
-        return;
+        Cache.info("REMOVEME skipping sequence " + seq.getName());
+        continue;
       }
+      Cache.info("REMOVEME Attempting to highlight sequence "
+              + seq.getName() + " at postition " + pos);
+      ssm.mouseOverSequence(seq, pos, -1, null);
     }
 
   }
diff --git a/src/jalview/rest/SelectSequencesEndpoint.java b/src/jalview/rest/SelectSequencesEndpoint.java
new file mode 100644 (file)
index 0000000..1a8c441
--- /dev/null
@@ -0,0 +1,108 @@
+package jalview.rest;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+
+public class SelectSequencesEndpoint extends AbstractEndpoint
+{
+  public SelectSequencesEndpoint(API api)
+  {
+    super(api, path, name, parameters, description);
+  }
+
+  protected static final String path = "selectsequences";
+
+  private static final String name = "Select sequence(s) positions";
+
+  private static final String parameters = "<sequence names>,<range>";
+
+  private static final String description = "Select the specified sequence(s) with the specified range";
+
+  public void processEndpoint(HttpServletRequest request,
+          HttpServletResponse response)
+  {
+    if (!checkParameters(request, response, 2))
+    {
+      return;
+    }
+    String[] parameters = getEndpointPathParameters(request);
+
+    String rangesString = parameters[1];
+    int[][] ranges = parseIntRanges(rangesString);
+    if (ranges == null || ranges.length < 2 || ranges[0].length < 1)
+    {
+      returnError(request, response,
+              "couldn't parse range '" + rangesString + "'");
+      return;
+    }
+    if (ranges[0].length > 1)
+    {
+      returnError(request, response,
+              "only provide 1 range '" + rangesString + "'");
+      return;
+    }
+    int start = ranges[0][0];
+    int end = ranges[1][0];
+
+    String sequenceNamesString = parameters[0];
+    List<String> sequenceNames = Arrays
+            .asList(sequenceNamesString.split(","));
+    String fromIdString = request.getParameter("fromId");
+
+    AlignFrame[] alignFrames;
+    if (fromIdString != null)
+    {
+      AlignFrame af = AlignFrame.getAlignFrameFromRestId(fromIdString);
+      if (af == null)
+      {
+        returnError(request, response,
+                "fromId value '" + fromIdString + "' results not found");
+        return;
+      }
+      alignFrames = new AlignFrame[] { af };
+    }
+    else
+    {
+      alignFrames = Desktop.getAlignFrames();
+    }
+    if (alignFrames == null)
+      return;
+    for (int i = 0; i < alignFrames.length; i++)
+    {
+      AlignFrame af = alignFrames[i];
+      List<AlignmentViewPanel> aps = (List<AlignmentViewPanel>) af
+              .getAlignPanels();
+      for (AlignmentViewPanel ap : aps)
+      {
+        AlignViewportI avp = ap.getAlignViewport();
+        // ap.getAlignViewport().getSequenceSetId()
+        AlignmentI al = ap.getAlignment();
+        List<SequenceI> seqs = (List<SequenceI>) al.getSequences();
+        SequenceGroup stretchGroup = new SequenceGroup();
+        for (SequenceI seq : seqs)
+        {
+          if (sequenceNames.contains(seq.getName()))
+          {
+            stretchGroup.addSequence(seq, false);
+          }
+        }
+        stretchGroup.setStartRes(start);
+        stretchGroup.setEndRes(end);
+        avp.setSelectionGroup(stretchGroup);
+        ap.paintAlignment(false, false);
+        avp.sendSelection();
+      }
+    }
+  }
+}
\ No newline at end of file