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