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