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