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