JAL-3851 merged to develop 2022-03-22
[jalview.git] / src / jalview / gui / SequenceFetcher.java
index 323fef9..7d74f82 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.api.FeatureSettingsModelI;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.SequenceI;
-import jalview.fts.core.GFTSPanel;
-import jalview.fts.service.pdb.PDBFTSPanel;
-import jalview.fts.service.uniprot.UniprotFTSPanel;
-import jalview.io.FileFormatI;
-import jalview.io.gff.SequenceOntologyI;
-import jalview.util.DBRefUtils;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -46,6 +31,7 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
@@ -57,6 +43,23 @@ import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 import javax.swing.SwingConstants;
 
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.core.GFTSPanel;
+import jalview.fts.service.pdb.PDBFTSPanel;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSPanel;
+import jalview.fts.service.uniprot.UniprotFTSPanel;
+import jalview.io.FileFormatI;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.DBRefUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.seqfetcher.DbSourceProxy;
+
 /**
  * A panel where the use may choose a database source, and enter one or more
  * accessions, to retrieve entries from the database.
@@ -66,11 +69,50 @@ import javax.swing.SwingConstants;
  */
 public class SequenceFetcher extends JPanel implements Runnable
 {
+  private class StringPair
+  {
+    private String key;
+
+    private String display;
+
+    public StringPair(String s1, String s2)
+    {
+      key = s1;
+      display = s2;
+    }
+
+    public StringPair(String s)
+    {
+      this(s, s);
+    }
+
+    public String getKey()
+    {
+      return key;
+    }
+
+    public String getDisplay()
+    {
+      return display;
+    }
+
+    @Override
+    public String toString()
+    {
+      return display;
+    }
+
+    public boolean equals(StringPair other)
+    {
+      return other.key == this.key;
+    }
+  }
+
   private static jalview.ws.SequenceFetcher sfetch = null;
 
   JLabel exampleAccession;
 
-  JComboBox<String> database;
+  JComboBox<StringPair> database;
 
   JCheckBox replacePunctuation;
 
@@ -127,10 +169,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;
@@ -140,13 +190,13 @@ public class SequenceFetcher extends JPanel implements Runnable
       alignFrame = (AlignFrame) progressIndicator;
     }
 
-    jbInit(selectedDb);
+    jbInit(selectedDb, interactive);
     textArea.setText(queryString);
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, 
-               Platform.isAMacAndNotJS() ? 240 : 180);
+    Desktop.addInternalFrame(frame, getFrameTitle(), true, 400,
+            Platform.isAMacAndNotJS() ? 240 : 180);
   }
 
   private String getFrameTitle()
@@ -157,21 +207,31 @@ 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());
 
     database = new JComboBox<>();
     database.setFont(JvSwingUtils.getLabelFont());
-    database.setPrototypeDisplayValue("ENSEMBLGENOMES   ");
+    StringPair instructionItem = new StringPair(
+            MessageManager.getString("action.select_ddbb"));
+    database.setPrototypeDisplayValue(instructionItem);
     String[] sources = new jalview.ws.SequenceFetcher().getSupportedDb();
     Arrays.sort(sources, String.CASE_INSENSITIVE_ORDER);
-    database.addItem(MessageManager.getString("action.select_ddbb"));
+    database.addItem(instructionItem);
     for (String source : sources)
     {
-      database.addItem(source);
+      List<DbSourceProxy> slist = sfetch.getSourceProxy(source);
+      if (slist.size() == 1 && slist.get(0) != null)
+      {
+        database.addItem(new StringPair(source, slist.get(0).getDbName()));
+      }
+      else
+      {
+        database.addItem(new StringPair(source));
+      }
     }
-    database.setSelectedItem(selectedDb);
+    setDatabaseSelectedItem(selectedDb);
     if (database.getSelectedIndex() == -1)
     {
       database.setSelectedIndex(0);
@@ -182,7 +242,8 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        String currentSelection = (String) database.getSelectedItem();
+        String currentSelection = ((StringPair) database.getSelectedItem())
+                .getKey();
         updateExampleQuery(currentSelection);
 
         if ("pdb".equalsIgnoreCase(currentSelection))
@@ -195,6 +256,11 @@ public class SequenceFetcher extends JPanel implements Runnable
           frame.dispose();
           new UniprotFTSPanel(SequenceFetcher.this);
         }
+        else if ("3d-beacons".equalsIgnoreCase(currentSelection))
+        {
+          frame.dispose();
+          new TDBeaconsFTSPanel(SequenceFetcher.this);
+        }
         else
         {
           otherSourceAction();
@@ -299,11 +365,36 @@ 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);
   }
 
+  private void setDatabaseSelectedItem(String db)
+  {
+    for (int i = 0; i < database.getItemCount(); i++)
+    {
+      StringPair sp = database.getItemAt(i);
+      if (sp != null && db != null && db.equals(sp.getKey()))
+      {
+        database.setSelectedIndex(i);
+        return;
+      }
+    }
+  }
+
   /**
    * Answers a semi-colon-delimited string with the example query or queries for
    * the selected database
@@ -375,7 +466,8 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   protected void example_actionPerformed()
   {
-    String eq = getExampleQueries((String) database.getSelectedItem());
+    String eq = getExampleQueries(
+            ((StringPair) database.getSelectedItem()).getKey());
     textArea.setText(eq);
     repaint();
   }
@@ -395,7 +487,7 @@ public class SequenceFetcher extends JPanel implements Runnable
    * 
    * @param e
    */
-  protected void close_actionPerformed(ActionEvent e)
+  public void close_actionPerformed(ActionEvent e)
   {
     try
     {
@@ -414,19 +506,25 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   public void ok_actionPerformed()
   {
+    ok_actionPerformed(false, null);
+  }
+
+  public CompletableFuture<Void> ok_actionPerformed(boolean returnFuture,
+          String id)
+  {
     /*
      * tidy inputs and check there is something to search for
      */
-    String text = textArea.getText();
-    final String t0 = text;
+    String t0 = textArea.getText();
+    String text = t0.trim();
     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
     {
       text = text.replace(",", ";");
     }
     text = text.replaceAll("(\\s|[; ])+", ";");
-    if (!t0.equals(text)) 
+    if (!t0.equals(text))
     {
-         textArea.setText(text);
+      textArea.setText(text);
     }
     if (text.isEmpty())
     {
@@ -434,16 +532,33 @@ public class SequenceFetcher extends JPanel implements Runnable
       showErrorMessage(
               "Please enter a (semi-colon separated list of) database id(s)");
       resetDialog();
-      return;
+      return null;
     }
+    if (database.getSelectedIndex() == 0)
+    {
+      // todo i18n
+      showErrorMessage("Please choose a database");
+      resetDialog();
+      return null;
+    }
+
     exampleBtn.setEnabled(false);
     textArea.setEnabled(false);
     okBtn.setEnabled(false);
     closeBtn.setEnabled(false);
     backBtn.setEnabled(false);
 
-    Thread worker = new Thread(this);
-    worker.start();
+    CompletableFuture<Void> worker = CompletableFuture
+            .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()
@@ -458,13 +573,18 @@ 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<>();
     List<AlignmentI> presult = new ArrayList<>();
     List<AlignmentI> aresult = new ArrayList<>();
-    List<DbSourceProxy> sources = sfetch
-            .getSourceProxy((String) database.getSelectedItem());
+    List<DbSourceProxy> sources = sfetch.getSourceProxy(
+            ((StringPair) database.getSelectedItem()).getKey());
     Iterator<DbSourceProxy> proxies = sources.iterator();
     String[] qries = textArea.getText().trim().split(";");
     List<String> nextFetch = Arrays.asList(qries);
@@ -516,23 +636,25 @@ public class SequenceFetcher extends JPanel implements Runnable
       } catch (Exception e)
       {
         showErrorMessage("Error retrieving " + textArea.getText() + " from "
-                + database.getSelectedItem());
+                + ((StringPair) database.getSelectedItem()).getDisplay());
         // error
         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
         System.err.println("Retrieval failed for source ='"
-                + database.getSelectedItem() + "' and query\n'"
-                + textArea.getText() + "'\n");
+                + ((StringPair) database.getSelectedItem()).getDisplay()
+                + "' and query\n'" + textArea.getText() + "'\n");
         e.printStackTrace();
       } catch (OutOfMemoryError e)
       {
         showErrorMessage("Out of Memory when retrieving "
-                + textArea.getText() + " from " + database.getSelectedItem()
+                + textArea.getText() + " from "
+                + ((StringPair) database.getSelectedItem()).getDisplay()
                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
         e.printStackTrace();
       } catch (Error e)
       {
         showErrorMessage("Serious Error retrieving " + textArea.getText()
-                + " from " + database.getSelectedItem());
+                + " from "
+                + ((StringPair) database.getSelectedItem()).getDisplay());
         e.printStackTrace();
       }
 
@@ -595,9 +717,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.
@@ -625,6 +748,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       showErrorMessage(sb.toString());
     }
     resetDialog();
+    return returnAlignFrame ? af : null;
   }
 
   /**
@@ -679,7 +803,8 @@ public class SequenceFetcher extends JPanel implements Runnable
     } catch (OutOfMemoryError oome)
     {
       new OOMWarning("fetching " + multiacc + " from "
-              + database.getSelectedItem(), oome, this);
+              + ((StringPair) database.getSelectedItem()).getDisplay(),
+              oome, this);
     }
   }
 
@@ -730,7 +855,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
     } catch (Exception e)
     {
-      Cache.log.info("Error retrieving " + accession + " from "
+      Console.info("Error retrieving " + accession + " from "
               + proxy.getDbName(), e);
     }
     return success;
@@ -753,12 +878,12 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     for (String q : queries)
     {
-       // BH 2019.01.25 dbr is never used.
-//      DBRefEntry dbr = new DBRefEntry();
-//      dbr.setSource(proxy.getDbSource());
-//      dbr.setVersion(null);
+      // BH 2019.01.25 dbr is never used.
+      // DBRefEntry dbr = new DBRefEntry();
+      // dbr.setSource(proxy.getDbSource());
+      // dbr.setVersion(null);
       String accId = proxy.getAccessionIdFromQuery(q);
-//      dbr.setAccessionId(accId);
+      // dbr.setAccessionId(accId);
       boolean rfound = false;
       for (int r = 0, nr = rs.length; r < nr; r++)
       {
@@ -787,23 +912,34 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   public String getDefaultRetrievalTitle()
   {
-    return "Retrieved from " + database.getSelectedItem();
+    return "Retrieved from "
+            + ((StringPair) database.getSelectedItem()).getDisplay();
   }
 
-  AlignmentI parseResult(AlignmentI al, String title,
+  /**
+   * constructs an alignment frame given the data and metadata
+   * 
+   * @param al
+   * @param title
+   * @param currentFileFormat
+   * @param preferredFeatureColours
+   * @return the alignment
+   */
+  public 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)
         {
@@ -823,10 +959,7 @@ public class SequenceFetcher extends JPanel implements Runnable
           }
         }
 
-        if (preferredFeatureColours != null)
-        {
-          af.getViewport().applyFeaturesStyle(preferredFeatureColours);
-        }
+        af.getViewport().applyFeaturesStyle(preferredFeatureColours);
         if (Cache.getDefault("HIDE_INTRONS", true))
         {
           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
@@ -846,10 +979,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)