JAL-3269 ready for testing embedded interface
[jalview.git] / src / jalview / bin / JalviewAppLoader.java
index 8029aea..51a0330 100644 (file)
@@ -1,19 +1,38 @@
 package jalview.bin;
 
+import jalview.api.AlignFrameI;
 import jalview.api.JalviewApp;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.Desktop;
 import jalview.io.AnnotationFile;
+import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
+import jalview.io.FeaturesFile;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.IdentifyFile;
 import jalview.io.JPredFile;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
+import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
 
+import java.awt.EventQueue;
+import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
@@ -33,7 +52,19 @@ public class JalviewAppLoader
 
   private boolean debug;
 
-  private String separator;
+  String separator = "\u00AC"; // JalviewLite note: the default used to
+                                       // be '|', but many sequence IDS include
+                                       // pipes.
+
+  public String getSeparator()
+  {
+    return separator;
+  }
+
+  public void setSeparator(String separator)
+  {
+    this.separator = separator;
+  }
 
   public JalviewAppLoader(boolean debug)
   {
@@ -491,11 +522,11 @@ public class JalviewAppLoader
     {
       resolvedPath = directoryPath + targetPath;
     }
-    if (JalviewLite.debug)
-    {
-      System.err.println(
-              "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
-    }
+    // if (debug)
+    // {
+    // System.err.println(
+    // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
+    // }
     return resolvedPath;
   }
 
@@ -730,4 +761,723 @@ public class JalviewAppLoader
     return arrayToSeparatorList(array, separator);
   }
 
+  public String getSelectedSequencesFrom(AlignFrameI alf, String sep)
+  {
+    StringBuffer result = new StringBuffer("");
+    if (sep == null || sep.length() == 0)
+    {
+      sep = separator; // "+0x00AC;
+    }
+    AlignViewport v = ((AlignFrame) alf).getViewport();
+    if (v.getSelectionGroup() != null)
+    {
+      SequenceI[] seqs = v.getSelectionGroup()
+              .getSequencesInOrder(v.getAlignment());
+
+      for (int i = 0; i < seqs.length; i++)
+      {
+        result.append(seqs[i].getName());
+        result.append(sep);
+      }
+    }
+
+    return result.toString();
+  }
+
+  public void setFeatureGroupStateOn(final AlignFrameI alf,
+          final String groups, boolean state)
+  {
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        ((AlignFrame) alf).setFeatureGroupState(
+                separatorListToArray(groups, separator), state);
+      }
+    });
+  }
+
+  public String getFeatureGroupsOfStateOn(AlignFrameI alf, boolean visible)
+  {
+    return arrayToSeparatorList(
+            ((AlignFrame) alf).getFeatureGroupsOfState(visible));
+  }
+
+  public void scrollViewToIn(final AlignFrameI alf, final String topRow,
+          final String leftHandColumn)
+  {
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          ((AlignFrame) alf).scrollTo(new Integer(topRow).intValue(),
+                  new Integer(leftHandColumn).intValue());
+
+        } catch (Exception ex)
+        {
+          System.err.println("Couldn't parse integer arguments (topRow='"
+                  + topRow + "' and leftHandColumn='" + leftHandColumn
+                  + "')");
+          ex.printStackTrace();
+        }
+      }
+    });
+  }
+
+  public void scrollViewToRowIn(final AlignFrameI alf, final String topRow)
+  {
+
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          ((AlignFrame) alf).scrollToRow(new Integer(topRow).intValue());
+
+        } catch (Exception ex)
+        {
+          System.err.println("Couldn't parse integer arguments (topRow='"
+                  + topRow + "')");
+          ex.printStackTrace();
+        }
+
+      }
+    });
+  }
+
+  public void scrollViewToColumnIn(final AlignFrameI alf,
+          final String leftHandColumn)
+  {
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+
+      @Override
+      public void run()
+      {
+        try
+        {
+          ((AlignFrame) alf)
+                  .scrollToColumn(new Integer(leftHandColumn).intValue());
+
+        } catch (Exception ex)
+        {
+          System.err.println(
+                  "Couldn't parse integer arguments (leftHandColumn='"
+                          + leftHandColumn + "')");
+          ex.printStackTrace();
+        }
+      }
+    });
+
+  }
+
+  public boolean addPdbFile(AlignFrameI alf, String sequenceId,
+          String pdbEntryString, String pdbFile)
+  {
+    AlignFrame alFrame = (AlignFrame) alf;
+    SequenceI toaddpdb = alFrame.getViewport().getAlignment()
+            .findName(sequenceId);
+    boolean needtoadd = false;
+    if (toaddpdb != null)
+    {
+      Vector<PDBEntry> pdbe = toaddpdb.getAllPDBEntries();
+      PDBEntry pdbentry = null;
+      if (pdbe != null && pdbe.size() > 0)
+      {
+        for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
+        {
+          pdbentry = pdbe.elementAt(pe);
+          if (!pdbentry.getId().equals(pdbEntryString)
+                  && !pdbentry.getFile().equals(pdbFile))
+          {
+            pdbentry = null;
+          }
+          else
+          {
+            continue;
+          }
+        }
+      }
+      if (pdbentry == null)
+      {
+        pdbentry = new PDBEntry();
+        pdbentry.setId(pdbEntryString);
+        pdbentry.setFile(pdbFile);
+        needtoadd = true; // add this new entry to sequence.
+      }
+      // resolve data source
+      // TODO: this code should be a refactored to an io package
+      DataSourceType protocol = AppletFormatAdapter.resolveProtocol(pdbFile,
+              FileFormat.PDB);
+      if (protocol == null)
+      {
+        return false;
+      }
+      if (needtoadd)
+      {
+        pdbentry.setProperty("protocol", protocol);
+        toaddpdb.addPDBId(pdbentry);
+        alFrame.alignPanel.getStructureSelectionManager()
+                .registerPDBEntry(pdbentry);
+      }
+    }
+    return true;
+  }
+
+  public AlignFrameI loadAlignment(String text, int width, int height,
+          String title)
+  {
+    AlignmentI al = null;
+
+    try
+    {
+      FileFormatI format = new IdentifyFile().identify(text,
+              DataSourceType.PASTE);
+      al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
+              format);
+      if (al.getHeight() > 0)
+      {
+        return new AlignFrame(al, width, height, title);
+      }
+    } catch (IOException ex)
+    {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+
+  public String getFeatureGroupsOn(AlignFrameI alf)
+  {
+    return arrayToSeparatorList(
+            ((AlignFrame) alf).getFeatureGroups());
+  }
+
+  public void highlightIn(final AlignFrameI alf, final String sequenceId,
+          final String position, final String alignedPosition)
+  {
+    // TODO: could try to highlight in all alignments if alf==null
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            ((AlignFrame) alf).getViewport().getAlignment()
+                    .getSequencesArray());
+    final SequenceI sq = matcher.findIdMatch(sequenceId);
+    if (sq != null)
+    {
+      int apos = -1;
+      try
+      {
+        apos = new Integer(position).intValue();
+        apos--;
+      } catch (NumberFormatException ex)
+      {
+        return;
+      }
+      final int pos = apos;
+      // use vamsas listener to broadcast to all listeners in scope
+      if (alignedPosition != null && (alignedPosition.trim().length() == 0
+              || alignedPosition.toLowerCase().indexOf("false") > -1))
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
+          }
+        });
+      }
+      else
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, pos, null);
+          }
+        });
+      }
+    }
+  }
+
+  public void selectIn(final AlignFrameI alf, String sequenceIds,
+          String columns, String sep)
+  {
+    if (sep == null || sep.length() == 0)
+    {
+      sep = separator;
+    }
+    else
+    {
+      if (debug)
+      {
+        System.err.println("Selecting region using separator string '"
+                + separator + "'");
+      }
+    }
+    // deparse fields
+    String[] ids = JalviewAppLoader.separatorListToArray(sequenceIds, sep);
+    String[] cols = JalviewAppLoader.separatorListToArray(columns, sep);
+    final SequenceGroup sel = new SequenceGroup();
+    final ColumnSelection csel = new ColumnSelection();
+    AlignmentI al = ((AlignFrame) alf).getViewport().getAlignment();
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            ((AlignFrame) alf).getViewport().getAlignment()
+                    .getSequencesArray());
+    int start = 0, end = al.getWidth(), alw = al.getWidth();
+    boolean seqsfound = true;
+    if (ids != null && ids.length > 0)
+    {
+      seqsfound = false;
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          seqsfound = true;
+          sel.addSequence(sq, false);
+        }
+      }
+    }
+    boolean inseqpos = false;
+    if (cols != null && cols.length > 0)
+    {
+      boolean seset = false;
+      for (int i = 0; i < cols.length; i++)
+      {
+        String cl = cols[i].trim();
+        if (cl.length() == 0)
+        {
+          continue;
+        }
+        int p;
+        if ((p = cl.indexOf("-")) > -1)
+        {
+          int from = -1, to = -1;
+          try
+          {
+            from = new Integer(cl.substring(0, p)).intValue();
+            from--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse first integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          try
+          {
+            to = new Integer(cl.substring(p + 1)).intValue();
+            to--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse second integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          if (from >= 0 && to >= 0)
+          {
+            // valid range
+            if (from < to)
+            {
+              int t = to;
+              to = from;
+              to = t;
+            }
+            if (!seset)
+            {
+              start = from;
+              end = to;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > from)
+              {
+                start = from;
+              }
+              if (end < to)
+              {
+                end = to;
+              }
+            }
+            for (int r = from; r <= to; r++)
+            {
+              if (r >= 0 && r < alw)
+              {
+                csel.addElement(r);
+              }
+            }
+            if (debug)
+            {
+              System.err.println("Range '" + cl + "' deparsed as [" + from
+                      + "," + to + "]");
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Range '" + cl
+                    + "' deparsed as [" + from + "," + to + "]");
+          }
+        }
+        else
+        {
+          int r = -1;
+          try
+          {
+            r = new Integer(cl).intValue();
+            r--;
+          } catch (NumberFormatException ex)
+          {
+            if (cl.toLowerCase().equals("sequence"))
+            {
+              // we are in the dataset sequence's coordinate frame.
+              inseqpos = true;
+            }
+            else
+            {
+              System.err.println(
+                      "ERROR: Couldn't parse integer from point selection element of column selection string '"
+                              + cl + "'");
+              return;
+            }
+          }
+          if (r >= 0 && r <= alw)
+          {
+            if (!seset)
+            {
+              start = r;
+              end = r;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > r)
+              {
+                start = r;
+              }
+              if (end < r)
+              {
+                end = r;
+              }
+            }
+            csel.addElement(r);
+            if (debug)
+            {
+              System.err.println("Point selection '" + cl
+                      + "' deparsed as [" + r + "]");
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Point selection '" + cl
+                    + "' deparsed as [" + r + "]");
+          }
+        }
+      }
+    }
+    if (seqsfound)
+    {
+      // we only propagate the selection when it was the null selection, or the
+      // given sequences were found in the alignment.
+      if (inseqpos && sel.getSize() > 0)
+      {
+        // assume first sequence provides reference frame ?
+        SequenceI rs = sel.getSequenceAt(0);
+        start = rs.findIndex(start);
+        end = rs.findIndex(end);
+        List<Integer> cs = new ArrayList<>(csel.getSelected());
+        csel.clear();
+        for (Integer selectedCol : cs)
+        {
+          csel.addElement(rs.findIndex(selectedCol));
+        }
+      }
+      sel.setStartRes(start);
+      sel.setEndRes(end);
+      EventQueue.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          ((AlignFrame) alf).select(sel, csel, ((AlignFrame) alf)
+                  .getCurrentView().getAlignment().getHiddenColumns());
+        }
+      });
+    }
+  }
+
+  public String getAlignmentOrderFrom(AlignFrameI alf, String sep)
+  {
+    AlignmentI alorder = ((AlignFrame) alf).getViewport().getAlignment();
+    String[] order = new String[alorder.getHeight()];
+    for (int i = 0; i < order.length; i++)
+    {
+      order[i] = alorder.getSequenceAt(i).getName();
+    }
+    return arrayToSeparatorList(order, sep);
+  }
+
+  public String getSelectedSequencesAsAlignmentFrom(AlignFrameI alf,
+          String format, String suffix)
+  {
+    try
+    {
+      AlignViewport vp = ((AlignFrame) alf).getViewport();
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      boolean seqlimits = (suffix == null
+              || suffix.equalsIgnoreCase("true"));
+      if (vp.getSelectionGroup() != null)
+      {
+        // JBPNote: getSelectionAsNewSequence behaviour has changed - this
+        // method now returns a full copy of sequence data
+        // TODO consider using getSequenceSelection instead here
+        String reply = new AppletFormatAdapter().formatSequences(theFormat,
+                new Alignment(vp.getSelectionAsNewSequence()),
+                seqlimits);
+        return reply;
+      }
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+    return "";
+  }
+
+  public String orderAlignmentBy(AlignFrameI alf, String order,
+          String undoName, String sep)
+  {
+    if (sep == null || sep.length() == 0)
+    {
+      sep = separator;
+    }
+    String[] ids = JalviewAppLoader.separatorListToArray(order, sep);
+    SequenceI[] sqs = null;
+    if (ids != null && ids.length > 0)
+    {
+      jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+              ((AlignFrame) alf).getViewport().getAlignment()
+                      .getSequencesArray());
+      int s = 0;
+      sqs = new SequenceI[ids.length];
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          sqs[s++] = sq;
+        }
+      }
+      if (s > 0)
+      {
+        SequenceI[] sqq = new SequenceI[s];
+        System.arraycopy(sqs, 0, sqq, 0, s);
+        sqs = sqq;
+      }
+      else
+      {
+        sqs = null;
+      }
+    }
+    if (sqs == null)
+    {
+      return "";
+    }
+    ;
+    final AlignmentOrder aorder = new AlignmentOrder(sqs);
+
+    if (undoName != null && undoName.trim().length() == 0)
+    {
+      undoName = null;
+    }
+    final String _undoName = undoName;
+    // TODO: deal with synchronization here: cannot raise any events until after
+    // this has returned.
+    return ((AlignFrame) alf).sortBy(aorder, _undoName) ? "true" : "";
+  }
+
+  public String getAlignmentFrom(AlignFrameI alf, String format,
+          String suffix)
+  {
+    try
+    {
+      boolean seqlimits = (suffix == null
+              || suffix.equalsIgnoreCase("true"));
+
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      String reply = new AppletFormatAdapter().formatSequences(theFormat,
+              ((AlignFrame) alf).getViewport().getAlignment(), seqlimits);
+      return reply;
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+  }
+
+  public void loadAnnotationFrom(AlignFrameI alf, String annotation)
+  {
+    if (new AnnotationFile().annotateAlignmentView(
+            ((AlignFrame) alf).getViewport(), annotation,
+            DataSourceType.PASTE))
+    {
+      ((AlignFrame) alf).alignPanel.fontChanged();
+      ((AlignFrame) alf).alignPanel.setScrollValues(0, 0);
+    }
+    else
+    {
+      ((AlignFrame) alf).parseFeaturesFile(annotation,
+              DataSourceType.PASTE);
+    }
+  }
+
+  public boolean loadFeaturesFrom(AlignFrameI alf, String features,
+          boolean autoenabledisplay)
+  {
+    boolean ret = ((AlignFrame) alf).parseFeaturesFile(features,
+            DataSourceType.PASTE);
+    if (!ret)
+    {
+      return false;
+    }
+    if (autoenabledisplay)
+    {
+      ((AlignFrame) alf).getViewport().setShowSequenceFeatures(true);
+      // this next was for a checkbox in JalviewLite
+      // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
+    }
+    return true;
+  }
+
+  public String getFeaturesFrom(AlignFrameI alf, String format)
+  {
+    AlignFrame f = ((AlignFrame) alf);
+
+    String features;
+    FeaturesFile formatter = new FeaturesFile();
+    if (format.equalsIgnoreCase("Jalview"))
+    {
+      features = formatter.printJalviewFormat(
+              f.getViewport().getAlignment().getSequencesArray(),
+              f.alignPanel.getFeatureRenderer(), true);
+    }
+    else
+    {
+      features = formatter.printGffFormat(
+              f.getViewport().getAlignment().getSequencesArray(),
+              f.alignPanel.getFeatureRenderer(), true);
+    }
+
+    if (features == null)
+    {
+      features = "";
+    }
+    return features;
+
+  }
+
+  public String getAnnotationFrom(AlignFrameI alf)
+  {
+    AlignFrame f = (AlignFrame) alf;
+    String annotation = new AnnotationFile()
+            .printAnnotationsForView(f.getViewport());
+    return annotation;
+  }
+
+  public AlignFrameI newViewFrom(AlignFrameI alf, String name)
+  {
+    return (AlignFrameI) ((AlignFrame) alf).newView(name, true);
+  }
+
+  public String[] separatorListToArray(String list)
+  {
+    return separatorListToArray(list, separator);
+  }
+
+  public Object[] getSelectionForListener(AlignFrameI currentFrame,
+          SequenceGroup seqsel, ColumnSelection colsel,
+          HiddenColumns hidden, SelectionSource source, Object alignFrame)
+  {
+    // System.err.println("Testing selection event relay to
+    // jsfunction:"+_listener);
+    String setid = "";
+    AlignFrame src = (AlignFrame) alignFrame;
+    if (source != null)
+    {
+      if (source instanceof AlignViewport
+              && ((AlignFrame) currentFrame).getViewport() == source)
+      {
+        // should be valid if it just generated an event!
+        src = (AlignFrame) currentFrame;
+
+      }
+    }
+    String[] seqs = new String[] {};
+    String[] cols = new String[] {};
+    int strt = 0, end = (src == null) ? -1
+            : src.alignPanel.av.getAlignment().getWidth();
+    if (seqsel != null && seqsel.getSize() > 0)
+    {
+      seqs = new String[seqsel.getSize()];
+      for (int i = 0; i < seqs.length; i++)
+      {
+        seqs[i] = seqsel.getSequenceAt(i).getName();
+      }
+      if (strt < seqsel.getStartRes())
+      {
+        strt = seqsel.getStartRes();
+      }
+      if (end == -1 || end > seqsel.getEndRes())
+      {
+        end = seqsel.getEndRes();
+      }
+    }
+    if (colsel != null && !colsel.isEmpty())
+    {
+      if (end == -1)
+      {
+        end = colsel.getMax() + 1;
+      }
+      cols = new String[colsel.getSelected().size()];
+      for (int i = 0; i < cols.length; i++)
+      {
+        cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
+      }
+    }
+    else
+    {
+      if (seqsel != null && seqsel.getSize() > 0)
+      {
+        // send a valid range, otherwise we send the empty selection
+        cols = new String[2];
+        cols[0] = "" + (1 + strt) + "-" + (1 + end);
+      }
+    }
+    return new Object[] { src, setid, arrayToSeparatorList(seqs),
+        arrayToSeparatorList(cols) };
+  }
+
 }
\ No newline at end of file