JAL-3851 merged to develop 2022-03-22
[jalview.git] / src / jalview / gui / SequenceFetcher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import java.awt.BorderLayout;
24 import java.awt.Font;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.KeyAdapter;
28 import java.awt.event.KeyEvent;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.concurrent.CompletableFuture;
35
36 import javax.swing.JButton;
37 import javax.swing.JCheckBox;
38 import javax.swing.JComboBox;
39 import javax.swing.JInternalFrame;
40 import javax.swing.JLabel;
41 import javax.swing.JPanel;
42 import javax.swing.JScrollPane;
43 import javax.swing.JTextArea;
44 import javax.swing.SwingConstants;
45
46 import jalview.api.FeatureSettingsModelI;
47 import jalview.bin.Cache;
48 import jalview.bin.Console;
49 import jalview.datamodel.AlignmentI;
50 import jalview.datamodel.DBRefEntry;
51 import jalview.datamodel.SequenceI;
52 import jalview.fts.core.GFTSPanel;
53 import jalview.fts.service.pdb.PDBFTSPanel;
54 import jalview.fts.service.threedbeacons.TDBeaconsFTSPanel;
55 import jalview.fts.service.uniprot.UniprotFTSPanel;
56 import jalview.io.FileFormatI;
57 import jalview.io.gff.SequenceOntologyI;
58 import jalview.util.DBRefUtils;
59 import jalview.util.MessageManager;
60 import jalview.util.Platform;
61 import jalview.ws.seqfetcher.DbSourceProxy;
62
63 /**
64  * A panel where the use may choose a database source, and enter one or more
65  * accessions, to retrieve entries from the database.
66  * <p>
67  * If the selected source is Uniprot or PDB, a free text search panel is opened
68  * instead to perform the search and selection.
69  */
70 public class SequenceFetcher extends JPanel implements Runnable
71 {
72   private class StringPair
73   {
74     private String key;
75
76     private String display;
77
78     public StringPair(String s1, String s2)
79     {
80       key = s1;
81       display = s2;
82     }
83
84     public StringPair(String s)
85     {
86       this(s, s);
87     }
88
89     public String getKey()
90     {
91       return key;
92     }
93
94     public String getDisplay()
95     {
96       return display;
97     }
98
99     @Override
100     public String toString()
101     {
102       return display;
103     }
104
105     public boolean equals(StringPair other)
106     {
107       return other.key == this.key;
108     }
109   }
110
111   private static jalview.ws.SequenceFetcher sfetch = null;
112
113   JLabel exampleAccession;
114
115   JComboBox<StringPair> database;
116
117   JCheckBox replacePunctuation;
118
119   JButton okBtn;
120
121   JButton exampleBtn;
122
123   JButton closeBtn;
124
125   JButton backBtn;
126
127   JTextArea textArea;
128
129   JInternalFrame frame;
130
131   IProgressIndicator guiWindow;
132
133   AlignFrame alignFrame;
134
135   GFTSPanel parentSearchPanel;
136
137   IProgressIndicator progressIndicator;
138
139   volatile boolean _isConstructing = false;
140
141   /**
142    * Returns the shared instance of the SequenceFetcher client
143    * 
144    * @return
145    */
146   public static jalview.ws.SequenceFetcher getSequenceFetcherSingleton()
147   {
148     if (sfetch == null)
149     {
150       sfetch = new jalview.ws.SequenceFetcher();
151     }
152     return sfetch;
153   }
154
155   /**
156    * Constructor given a client to receive any status or progress messages
157    * (currently either the Desktop, or an AlignFrame panel)
158    * 
159    * @param guiIndic
160    */
161   public SequenceFetcher(IProgressIndicator guiIndic)
162   {
163     this(guiIndic, null, null);
164   }
165
166   /**
167    * Constructor with specified database and accession(s) to retrieve
168    * 
169    * @param guiIndic
170    * @param selectedDb
171    * @param queryString
172    * @param interactive
173    */
174   public SequenceFetcher(IProgressIndicator guiIndic,
175           final String selectedDb, final String queryString)
176   {
177     this(guiIndic, selectedDb, queryString, true);
178   }
179
180   public SequenceFetcher(IProgressIndicator guiIndic,
181           final String selectedDb, final String queryString,
182           boolean interactive)
183   {
184     this.progressIndicator = guiIndic;
185     getSequenceFetcherSingleton();
186     this.guiWindow = progressIndicator;
187
188     if (progressIndicator instanceof AlignFrame)
189     {
190       alignFrame = (AlignFrame) progressIndicator;
191     }
192
193     jbInit(selectedDb, interactive);
194     textArea.setText(queryString);
195
196     frame = new JInternalFrame();
197     frame.setContentPane(this);
198     Desktop.addInternalFrame(frame, getFrameTitle(), true, 400,
199             Platform.isAMacAndNotJS() ? 240 : 180);
200   }
201
202   private String getFrameTitle()
203   {
204     return ((alignFrame == null)
205             ? MessageManager.getString("label.new_sequence_fetcher")
206             : MessageManager
207                     .getString("label.additional_sequence_fetcher"));
208   }
209
210   private void jbInit(String selectedDb, boolean interactive)
211   {
212     this.setLayout(new BorderLayout());
213
214     database = new JComboBox<>();
215     database.setFont(JvSwingUtils.getLabelFont());
216     StringPair instructionItem = new StringPair(
217             MessageManager.getString("action.select_ddbb"));
218     database.setPrototypeDisplayValue(instructionItem);
219     String[] sources = new jalview.ws.SequenceFetcher().getSupportedDb();
220     Arrays.sort(sources, String.CASE_INSENSITIVE_ORDER);
221     database.addItem(instructionItem);
222     for (String source : sources)
223     {
224       List<DbSourceProxy> slist = sfetch.getSourceProxy(source);
225       if (slist.size() == 1 && slist.get(0) != null)
226       {
227         database.addItem(new StringPair(source, slist.get(0).getDbName()));
228       }
229       else
230       {
231         database.addItem(new StringPair(source));
232       }
233     }
234     setDatabaseSelectedItem(selectedDb);
235     if (database.getSelectedIndex() == -1)
236     {
237       database.setSelectedIndex(0);
238     }
239     database.setMaximumRowCount(database.getItemCount());
240     database.addActionListener(new ActionListener()
241     {
242       @Override
243       public void actionPerformed(ActionEvent e)
244       {
245         String currentSelection = ((StringPair) database.getSelectedItem())
246                 .getKey();
247         updateExampleQuery(currentSelection);
248
249         if ("pdb".equalsIgnoreCase(currentSelection))
250         {
251           frame.dispose();
252           new PDBFTSPanel(SequenceFetcher.this);
253         }
254         else if ("uniprot".equalsIgnoreCase(currentSelection))
255         {
256           frame.dispose();
257           new UniprotFTSPanel(SequenceFetcher.this);
258         }
259         else if ("3d-beacons".equalsIgnoreCase(currentSelection))
260         {
261           frame.dispose();
262           new TDBeaconsFTSPanel(SequenceFetcher.this);
263         }
264         else
265         {
266           otherSourceAction();
267         }
268       }
269     });
270
271     exampleAccession = new JLabel("");
272     exampleAccession.setFont(new Font("Verdana", Font.BOLD, 11));
273     JLabel jLabel1 = new JLabel(MessageManager
274             .getString("label.separate_multiple_accession_ids"));
275     jLabel1.setFont(new Font("Verdana", Font.ITALIC, 11));
276     jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
277
278     replacePunctuation = new JCheckBox(
279             MessageManager.getString("label.replace_commas_semicolons"));
280     replacePunctuation.setHorizontalAlignment(SwingConstants.LEFT);
281     replacePunctuation.setFont(new Font("Verdana", Font.ITALIC, 11));
282     okBtn = new JButton(MessageManager.getString("action.ok"));
283     okBtn.addActionListener(new ActionListener()
284     {
285       @Override
286       public void actionPerformed(ActionEvent e)
287       {
288         ok_actionPerformed();
289       }
290     });
291     JButton clear = new JButton(MessageManager.getString("action.clear"));
292     clear.addActionListener(new ActionListener()
293     {
294       @Override
295       public void actionPerformed(ActionEvent e)
296       {
297         clear_actionPerformed();
298       }
299     });
300
301     exampleBtn = new JButton(MessageManager.getString("label.example"));
302     exampleBtn.addActionListener(new ActionListener()
303     {
304       @Override
305       public void actionPerformed(ActionEvent e)
306       {
307         example_actionPerformed();
308       }
309     });
310     closeBtn = new JButton(MessageManager.getString("action.cancel"));
311     closeBtn.addActionListener(new ActionListener()
312     {
313       @Override
314       public void actionPerformed(ActionEvent e)
315       {
316         close_actionPerformed(e);
317       }
318     });
319     backBtn = new JButton(MessageManager.getString("action.back"));
320     backBtn.addActionListener(new ActionListener()
321     {
322       @Override
323       public void actionPerformed(ActionEvent e)
324       {
325         parentSearchPanel.btn_back_ActionPerformed();
326       }
327     });
328     // back not visible unless embedded
329     backBtn.setVisible(false);
330
331     textArea = new JTextArea();
332     textArea.setFont(JvSwingUtils.getLabelFont());
333     textArea.setLineWrap(true);
334     textArea.addKeyListener(new KeyAdapter()
335     {
336       @Override
337       public void keyPressed(KeyEvent e)
338       {
339         if (e.getKeyCode() == KeyEvent.VK_ENTER)
340         {
341           ok_actionPerformed();
342         }
343       }
344     });
345
346     JPanel actionPanel = new JPanel();
347     actionPanel.add(backBtn);
348     actionPanel.add(exampleBtn);
349     actionPanel.add(clear);
350     actionPanel.add(okBtn);
351     actionPanel.add(closeBtn);
352
353     JPanel databasePanel = new JPanel();
354     databasePanel.setLayout(new BorderLayout());
355     databasePanel.add(database, BorderLayout.NORTH);
356     databasePanel.add(exampleAccession, BorderLayout.CENTER);
357     JPanel jPanel2a = new JPanel(new BorderLayout());
358     jPanel2a.add(jLabel1, BorderLayout.NORTH);
359     jPanel2a.add(replacePunctuation, BorderLayout.SOUTH);
360     databasePanel.add(jPanel2a, BorderLayout.SOUTH);
361
362     JPanel idsPanel = new JPanel();
363     idsPanel.setLayout(new BorderLayout(0, 5));
364     JScrollPane jScrollPane1 = new JScrollPane();
365     jScrollPane1.getViewport().add(textArea);
366     idsPanel.add(jScrollPane1, BorderLayout.CENTER);
367
368     // En/disable or show/hide interactive elements
369     database.setEnabled(interactive);
370     exampleAccession.setVisible(interactive);
371     replacePunctuation.setVisible(interactive);
372     okBtn.setVisible(interactive);
373     exampleBtn.setVisible(interactive);
374     closeBtn.setVisible(interactive);
375     backBtn.setVisible(interactive);
376     jLabel1.setVisible(interactive);
377     clear.setVisible(interactive);
378     textArea.setEnabled(interactive);
379
380     this.add(actionPanel, BorderLayout.SOUTH);
381     this.add(idsPanel, BorderLayout.CENTER);
382     this.add(databasePanel, BorderLayout.NORTH);
383   }
384
385   private void setDatabaseSelectedItem(String db)
386   {
387     for (int i = 0; i < database.getItemCount(); i++)
388     {
389       StringPair sp = database.getItemAt(i);
390       if (sp != null && db != null && db.equals(sp.getKey()))
391       {
392         database.setSelectedIndex(i);
393         return;
394       }
395     }
396   }
397
398   /**
399    * Answers a semi-colon-delimited string with the example query or queries for
400    * the selected database
401    * 
402    * @param db
403    * @return
404    */
405   protected String getExampleQueries(String db)
406   {
407     StringBuilder sb = new StringBuilder();
408     HashSet<String> hs = new HashSet<>();
409     for (DbSourceProxy dbs : sfetch.getSourceProxy(db))
410     {
411       String tq = dbs.getTestQuery();
412       if (hs.add(tq)) // not a duplicate source
413       {
414         if (sb.length() > 0)
415         {
416           sb.append(";");
417         }
418         sb.append(tq);
419       }
420     }
421     return sb.toString();
422   }
423
424   /**
425    * Action on selecting a database other than Uniprot or PDB is to enable or
426    * disable 'Replace commas', and await input in the query field
427    */
428   protected void otherSourceAction()
429   {
430     try
431     {
432       String eq = exampleAccession.getText();
433       // TODO this should be a property of the SequenceFetcher whether commas
434       // are allowed in the IDs...
435
436       boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
437       replacePunctuation.setEnabled(enablePunct);
438
439     } catch (Exception ex)
440     {
441       exampleAccession.setText("");
442       replacePunctuation.setEnabled(true);
443     }
444     repaint();
445   }
446
447   /**
448    * Sets the text of the example query to incorporate the example accession
449    * provided by the selected database source
450    * 
451    * @param selectedDatabase
452    * @return
453    */
454   protected String updateExampleQuery(String selectedDatabase)
455   {
456     String eq = getExampleQueries(selectedDatabase);
457     exampleAccession.setText(MessageManager
458             .formatMessage("label.example_query_param", new String[]
459             { eq }));
460     return eq;
461   }
462
463   /**
464    * Action on clicking the 'Example' button is to write the example accession
465    * as the query text field value
466    */
467   protected void example_actionPerformed()
468   {
469     String eq = getExampleQueries(
470             ((StringPair) database.getSelectedItem()).getKey());
471     textArea.setText(eq);
472     repaint();
473   }
474
475   /**
476    * Clears the query input field
477    */
478   protected void clear_actionPerformed()
479   {
480     textArea.setText("");
481     repaint();
482   }
483
484   /**
485    * Action on Close button is to close this frame, and also (if it is embedded
486    * in a search panel) to close the search panel
487    * 
488    * @param e
489    */
490   public void close_actionPerformed(ActionEvent e)
491   {
492     try
493     {
494       frame.setClosed(true);
495       if (parentSearchPanel != null)
496       {
497         parentSearchPanel.btn_cancel_ActionPerformed();
498       }
499     } catch (Exception ex)
500     {
501     }
502   }
503
504   /**
505    * Action on OK is to start the fetch for entered accession(s)
506    */
507   public void ok_actionPerformed()
508   {
509     ok_actionPerformed(false, null);
510   }
511
512   public CompletableFuture<Void> ok_actionPerformed(boolean returnFuture,
513           String id)
514   {
515     /*
516      * tidy inputs and check there is something to search for
517      */
518     String t0 = textArea.getText();
519     String text = t0.trim();
520     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
521     {
522       text = text.replace(",", ";");
523     }
524     text = text.replaceAll("(\\s|[; ])+", ";");
525     if (!t0.equals(text))
526     {
527       textArea.setText(text);
528     }
529     if (text.isEmpty())
530     {
531       // todo i18n
532       showErrorMessage(
533               "Please enter a (semi-colon separated list of) database id(s)");
534       resetDialog();
535       return null;
536     }
537     if (database.getSelectedIndex() == 0)
538     {
539       // todo i18n
540       showErrorMessage("Please choose a database");
541       resetDialog();
542       return null;
543     }
544
545     exampleBtn.setEnabled(false);
546     textArea.setEnabled(false);
547     okBtn.setEnabled(false);
548     closeBtn.setEnabled(false);
549     backBtn.setEnabled(false);
550
551     CompletableFuture<Void> worker = CompletableFuture
552             .runAsync(() -> runAndCacheAlignFrame(returnFuture, id));
553
554     return returnFuture ? worker : null;
555   }
556
557   private void runAndCacheAlignFrame(boolean cacheAlignFrame, String id)
558   {
559     AlignFrame af = this.run(cacheAlignFrame);
560     if (cacheAlignFrame && id != null && af != null)
561       af.cacheAlignFrameFromRestId(id);
562   }
563
564   private void resetDialog()
565   {
566     exampleBtn.setEnabled(true);
567     textArea.setEnabled(true);
568     okBtn.setEnabled(true);
569     closeBtn.setEnabled(true);
570     backBtn.setEnabled(parentSearchPanel != null);
571   }
572
573   @Override
574   public void run()
575   {
576     run(false);
577   }
578
579   public AlignFrame run(boolean returnAlignFrame)
580   {
581     boolean addToLast = false;
582     List<String> aresultq = new ArrayList<>();
583     List<String> presultTitle = new ArrayList<>();
584     List<AlignmentI> presult = new ArrayList<>();
585     List<AlignmentI> aresult = new ArrayList<>();
586     List<DbSourceProxy> sources = sfetch.getSourceProxy(
587             ((StringPair) database.getSelectedItem()).getKey());
588     Iterator<DbSourceProxy> proxies = sources.iterator();
589     String[] qries = textArea.getText().trim().split(";");
590     List<String> nextFetch = Arrays.asList(qries);
591     Iterator<String> en = Arrays.asList(new String[0]).iterator();
592     int nqueries = qries.length;
593
594     FeatureSettingsModelI preferredFeatureColours = null;
595     while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
596     {
597       if (!en.hasNext() && nextFetch.size() > 0)
598       {
599         en = nextFetch.iterator();
600         nqueries = nextFetch.size();
601         // save the remaining queries in the original array
602         qries = nextFetch.toArray(new String[nqueries]);
603         nextFetch = new ArrayList<>();
604       }
605
606       DbSourceProxy proxy = proxies.next();
607       try
608       {
609         // update status
610         guiWindow.setProgressBar(MessageManager.formatMessage(
611                 "status.fetching_sequence_queries_from", new String[]
612                 { Integer.valueOf(nqueries).toString(),
613                     proxy.getDbName() }),
614                 Thread.currentThread().hashCode());
615         if (proxy.getMaximumQueryCount() == 1)
616         {
617           /*
618            * proxy only handles one accession id at a time
619            */
620           while (en.hasNext())
621           {
622             String acc = en.next();
623             if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
624             {
625               nextFetch.add(acc);
626             }
627           }
628         }
629         else
630         {
631           /*
632            * proxy can fetch multiple accessions at one time
633            */
634           fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
635         }
636       } catch (Exception e)
637       {
638         showErrorMessage("Error retrieving " + textArea.getText() + " from "
639                 + ((StringPair) database.getSelectedItem()).getDisplay());
640         // error
641         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
642         System.err.println("Retrieval failed for source ='"
643                 + ((StringPair) database.getSelectedItem()).getDisplay()
644                 + "' and query\n'" + textArea.getText() + "'\n");
645         e.printStackTrace();
646       } catch (OutOfMemoryError e)
647       {
648         showErrorMessage("Out of Memory when retrieving "
649                 + textArea.getText() + " from "
650                 + ((StringPair) database.getSelectedItem()).getDisplay()
651                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
652         e.printStackTrace();
653       } catch (Error e)
654       {
655         showErrorMessage("Serious Error retrieving " + textArea.getText()
656                 + " from "
657                 + ((StringPair) database.getSelectedItem()).getDisplay());
658         e.printStackTrace();
659       }
660
661       // Stack results ready for opening in alignment windows
662       if (aresult != null && aresult.size() > 0)
663       {
664         FeatureSettingsModelI proxyColourScheme = proxy
665                 .getFeatureColourScheme();
666         if (proxyColourScheme != null)
667         {
668           preferredFeatureColours = proxyColourScheme;
669         }
670
671         AlignmentI ar = null;
672         if (proxy.isAlignmentSource())
673         {
674           addToLast = false;
675           // new window for each result
676           while (aresult.size() > 0)
677           {
678             presult.add(aresult.remove(0));
679             presultTitle.add(
680                     aresultq.remove(0) + " " + getDefaultRetrievalTitle());
681           }
682         }
683         else
684         {
685           String titl = null;
686           if (addToLast && presult.size() > 0)
687           {
688             ar = presult.remove(presult.size() - 1);
689             titl = presultTitle.remove(presultTitle.size() - 1);
690           }
691           // concatenate all results in one window
692           while (aresult.size() > 0)
693           {
694             if (ar == null)
695             {
696               ar = aresult.remove(0);
697             }
698             else
699             {
700               ar.append(aresult.remove(0));
701             }
702           }
703           addToLast = true;
704           presult.add(ar);
705           presultTitle.add(titl);
706         }
707       }
708       guiWindow.setProgressBar(
709               MessageManager.getString("status.finshed_querying"),
710               Thread.currentThread().hashCode());
711     }
712     guiWindow
713             .setProgressBar(
714                     (presult.size() > 0)
715                             ? MessageManager
716                                     .getString("status.parsing_results")
717                             : MessageManager.getString("status.processing"),
718                     Thread.currentThread().hashCode());
719     // process results
720     AlignFrame af = null;
721     while (presult.size() > 0)
722     {
723       af = parseResult(presult.remove(0), presultTitle.remove(0), null,
724               preferredFeatureColours);
725     }
726     // only remove visual delay after we finished parsing.
727     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
728     if (nextFetch.size() > 0)
729     {
730       StringBuffer sb = new StringBuffer();
731       sb.append("Didn't retrieve the following "
732               + (nextFetch.size() == 1 ? "query"
733                       : nextFetch.size() + " queries")
734               + ": \n");
735       int l = sb.length(), lr = 0;
736       for (String s : nextFetch)
737       {
738         if (l != sb.length())
739         {
740           sb.append("; ");
741         }
742         if (lr - sb.length() > 40)
743         {
744           sb.append("\n");
745         }
746         sb.append(s);
747       }
748       showErrorMessage(sb.toString());
749     }
750     resetDialog();
751     return returnAlignFrame ? af : null;
752   }
753
754   /**
755    * Tries to fetch one or more accession ids from the database proxy
756    * 
757    * @param proxy
758    * @param accessions
759    *          the queries to fetch
760    * @param aresultq
761    *          a successful queries list to add to
762    * @param aresult
763    *          a list of retrieved alignments to add to
764    * @param nextFetch
765    *          failed queries are added to this list
766    * @throws Exception
767    */
768   void fetchMultipleAccessions(DbSourceProxy proxy,
769           Iterator<String> accessions, List<String> aresultq,
770           List<AlignmentI> aresult, List<String> nextFetch) throws Exception
771   {
772     StringBuilder multiacc = new StringBuilder();
773     List<String> tosend = new ArrayList<>();
774     while (accessions.hasNext())
775     {
776       String nel = accessions.next();
777       tosend.add(nel);
778       multiacc.append(nel);
779       if (accessions.hasNext())
780       {
781         multiacc.append(proxy.getAccessionSeparator());
782       }
783     }
784
785     try
786     {
787       String query = multiacc.toString();
788       AlignmentI rslt = proxy.getSequenceRecords(query);
789       if (rslt == null || rslt.getHeight() == 0)
790       {
791         // no results - pass on all queries to next source
792         nextFetch.addAll(tosend);
793       }
794       else
795       {
796         aresultq.add(query);
797         aresult.add(rslt);
798         if (tosend.size() > 1)
799         {
800           checkResultForQueries(rslt, tosend, nextFetch, proxy);
801         }
802       }
803     } catch (OutOfMemoryError oome)
804     {
805       new OOMWarning("fetching " + multiacc + " from "
806               + ((StringPair) database.getSelectedItem()).getDisplay(),
807               oome, this);
808     }
809   }
810
811   /**
812    * Query for a single accession id via the database proxy
813    * 
814    * @param proxy
815    * @param accession
816    * @param aresultq
817    *          a list of successful queries to add to
818    * @param aresult
819    *          a list of retrieved alignments to add to
820    * @return true if the fetch was successful, else false
821    */
822   boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
823           List<String> aresultq, List<AlignmentI> aresult)
824   {
825     boolean success = false;
826     try
827     {
828       if (aresult != null)
829       {
830         try
831         {
832           // give the server a chance to breathe
833           Thread.sleep(5);
834         } catch (Exception e)
835         {
836           //
837         }
838       }
839
840       AlignmentI indres = null;
841       try
842       {
843         indres = proxy.getSequenceRecords(accession);
844       } catch (OutOfMemoryError oome)
845       {
846         new OOMWarning(
847                 "fetching " + accession + " from " + proxy.getDbName(),
848                 oome, this);
849       }
850       if (indres != null)
851       {
852         aresultq.add(accession);
853         aresult.add(indres);
854         success = true;
855       }
856     } catch (Exception e)
857     {
858       Console.info("Error retrieving " + accession + " from "
859               + proxy.getDbName(), e);
860     }
861     return success;
862   }
863
864   /**
865    * Checks which of the queries were successfully retrieved by searching the
866    * DBRefs of the retrieved sequences for a match. Any not found are added to
867    * the 'nextFetch' list.
868    * 
869    * @param rslt
870    * @param queries
871    * @param nextFetch
872    * @param proxy
873    */
874   void checkResultForQueries(AlignmentI rslt, List<String> queries,
875           List<String> nextFetch, DbSourceProxy proxy)
876   {
877     SequenceI[] rs = rslt.getSequencesArray();
878
879     for (String q : queries)
880     {
881       // BH 2019.01.25 dbr is never used.
882       // DBRefEntry dbr = new DBRefEntry();
883       // dbr.setSource(proxy.getDbSource());
884       // dbr.setVersion(null);
885       String accId = proxy.getAccessionIdFromQuery(q);
886       // dbr.setAccessionId(accId);
887       boolean rfound = false;
888       for (int r = 0, nr = rs.length; r < nr; r++)
889       {
890         if (rs[r] != null)
891         {
892           List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
893                   accId);
894           if (!found.isEmpty())
895           {
896             rfound = true;
897             break;
898           }
899         }
900       }
901       if (!rfound)
902       {
903         nextFetch.add(q);
904       }
905     }
906   }
907
908   /**
909    * 
910    * @return a standard title for any results retrieved using the currently
911    *         selected source and settings
912    */
913   public String getDefaultRetrievalTitle()
914   {
915     return "Retrieved from "
916             + ((StringPair) database.getSelectedItem()).getDisplay();
917   }
918
919   /**
920    * constructs an alignment frame given the data and metadata
921    * 
922    * @param al
923    * @param title
924    * @param currentFileFormat
925    * @param preferredFeatureColours
926    * @return the alignment
927    */
928   public AlignFrame parseResult(AlignmentI al, String title,
929           FileFormatI currentFileFormat,
930           FeatureSettingsModelI preferredFeatureColours)
931   {
932
933     AlignFrame af = alignFrame;
934     if (al != null && al.getHeight() > 0)
935     {
936       if (title == null)
937       {
938         title = getDefaultRetrievalTitle();
939       }
940       if (af == null)
941       {
942         af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
943                 AlignFrame.DEFAULT_HEIGHT);
944         if (currentFileFormat != null)
945         {
946           af.currentFileFormat = currentFileFormat;
947         }
948
949         List<SequenceI> alsqs = al.getSequences();
950         synchronized (alsqs)
951         {
952           for (SequenceI sq : alsqs)
953           {
954             if (sq.getFeatures().hasFeatures())
955             {
956               af.setShowSeqFeatures(true);
957               break;
958             }
959           }
960         }
961
962         af.getViewport().applyFeaturesStyle(preferredFeatureColours);
963         if (Cache.getDefault("HIDE_INTRONS", true))
964         {
965           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
966         }
967         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
968                 AlignFrame.DEFAULT_HEIGHT);
969
970         af.setStatus(MessageManager
971                 .getString("label.successfully_pasted_alignment_file"));
972
973         try
974         {
975           af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
976         } catch (Exception ex)
977         {
978         }
979       }
980       else
981       {
982         af.viewport.addAlignment(al, title);
983       }
984     }
985     return af;
986   }
987
988   void showErrorMessage(final String error)
989   {
990     resetDialog();
991     javax.swing.SwingUtilities.invokeLater(new Runnable()
992     {
993       @Override
994       public void run()
995       {
996         JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
997                 MessageManager.getString("label.error_retrieving_data"),
998                 JvOptionPane.WARNING_MESSAGE);
999       }
1000     });
1001   }
1002
1003   public IProgressIndicator getProgressIndicator()
1004   {
1005     return progressIndicator;
1006   }
1007
1008   public void setProgressIndicator(IProgressIndicator progressIndicator)
1009   {
1010     this.progressIndicator = progressIndicator;
1011   }
1012
1013   /**
1014    * Hide this panel (on clicking the database button to open the database
1015    * chooser)
1016    */
1017   void hidePanel()
1018   {
1019     frame.setVisible(false);
1020   }
1021
1022   public void setQuery(String ids)
1023   {
1024     textArea.setText(ids);
1025   }
1026
1027   /**
1028    * Called to modify the search panel for embedding as an alternative tab of a
1029    * free text search panel. The database choice list is hidden (since the
1030    * choice has been made), and a Back button is made visible (which reopens the
1031    * Sequence Fetcher panel).
1032    * 
1033    * @param parentPanel
1034    */
1035   public void embedIn(GFTSPanel parentPanel)
1036   {
1037     database.setVisible(false);
1038     backBtn.setVisible(true);
1039     parentSearchPanel = parentPanel;
1040   }
1041 }