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