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