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