Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / jalview / gui / SequenceFetcher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.api.FeatureSettingsModelI;
24 import jalview.bin.Cache;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.DBRefEntry;
27 import jalview.datamodel.SequenceFeature;
28 import jalview.datamodel.SequenceI;
29 import jalview.fts.service.pdb.PDBFTSPanel;
30 import jalview.fts.service.uniprot.UniprotFTSPanel;
31 import jalview.io.FileFormatI;
32 import jalview.io.gff.SequenceOntologyI;
33 import jalview.util.DBRefUtils;
34 import jalview.util.MessageManager;
35 import jalview.util.Platform;
36 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
37 import jalview.ws.seqfetcher.DbSourceProxy;
38
39 import java.awt.BorderLayout;
40 import java.awt.Font;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.KeyAdapter;
44 import java.awt.event.KeyEvent;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.Iterator;
49 import java.util.List;
50
51 import javax.swing.JButton;
52 import javax.swing.JCheckBox;
53 import javax.swing.JInternalFrame;
54 import javax.swing.JLabel;
55 import javax.swing.JPanel;
56 import javax.swing.JScrollPane;
57 import javax.swing.JTextArea;
58 import javax.swing.SwingConstants;
59 import javax.swing.tree.DefaultMutableTreeNode;
60
61 public class SequenceFetcher extends JPanel implements Runnable
62 {
63   JLabel dbeg = new JLabel();
64
65   JDatabaseTree database;
66
67   JButton databaseButt;
68
69   JLabel jLabel1 = new JLabel();
70
71   JCheckBox replacePunctuation = new JCheckBox();
72
73   JButton ok = new JButton();
74
75   JButton clear = new JButton();
76
77   JButton example = new JButton();
78
79   JButton close = new JButton();
80
81   JPanel jPanel1 = new JPanel();
82
83   JTextArea textArea = new JTextArea();
84
85   JScrollPane jScrollPane1 = new JScrollPane();
86
87   JPanel jPanel2 = new JPanel();
88
89   JPanel jPanel3 = new JPanel();
90
91   JPanel jPanel4 = new JPanel();
92
93   BorderLayout borderLayout1 = new BorderLayout();
94
95   BorderLayout borderLayout2 = new BorderLayout();
96
97   BorderLayout borderLayout3 = new BorderLayout();
98
99   JInternalFrame frame;
100
101   IProgressIndicator guiWindow;
102
103   AlignFrame alignFrame;
104
105   StringBuffer result;
106
107   final String noDbSelected = "-- Select Database --";
108
109   private static jalview.ws.SequenceFetcher sfetch = null;
110
111   private static long lastDasSourceRegistry = -3;
112
113   private static DasSourceRegistryI dasRegistry = null;
114
115   private static boolean _initingFetcher = false;
116
117   private static Thread initingThread = null;
118
119   public JTextArea getTextArea()
120   {
121     return textArea;
122   }
123
124   /**
125    * Blocking method that initialises and returns the shared instance of the
126    * SequenceFetcher client
127    * 
128    * @param guiWindow
129    *          - where the initialisation delay message should be shown
130    * @return the singleton instance of the sequence fetcher client
131    */
132   public static jalview.ws.SequenceFetcher getSequenceFetcherSingleton(
133           final IProgressIndicator guiWindow)
134   {
135     if (_initingFetcher && initingThread != null && initingThread.isAlive())
136     {
137       if (guiWindow != null)
138       {
139         guiWindow
140                 .setProgressBar(
141                         MessageManager
142                                 .getString("status.waiting_sequence_database_fetchers_init"),
143                         Thread.currentThread().hashCode());
144       }
145       // initting happening on another thread - so wait around to see if it
146       // finishes.
147       while (_initingFetcher && initingThread != null
148               && initingThread.isAlive())
149       {
150         try
151         {
152           Thread.sleep(10);
153         } catch (Exception e)
154         {
155         }
156         ;
157       }
158       if (guiWindow != null)
159       {
160         guiWindow
161                 .setProgressBar(
162                         MessageManager
163                                 .getString("status.waiting_sequence_database_fetchers_init"),
164                         Thread.currentThread().hashCode());
165       }
166     }
167     if (sfetch == null
168             || dasRegistry != Cache.getDasSourceRegistry()
169             || lastDasSourceRegistry != (Cache.getDasSourceRegistry()
170                     .getDasRegistryURL() + Cache.getDasSourceRegistry()
171                     .getLocalSourceString()).hashCode())
172     {
173       _initingFetcher = true;
174       initingThread = Thread.currentThread();
175       /**
176        * give a visual indication that sequence fetcher construction is occuring
177        */
178       if (guiWindow != null)
179       {
180         guiWindow.setProgressBar(MessageManager
181                 .getString("status.init_sequence_database_fetchers"),
182                 Thread.currentThread().hashCode());
183       }
184       dasRegistry = Cache.getDasSourceRegistry();
185       dasRegistry.refreshSources();
186
187       jalview.ws.SequenceFetcher sf = new jalview.ws.SequenceFetcher();
188       if (guiWindow != null)
189       {
190         guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
191       }
192       lastDasSourceRegistry = (dasRegistry.getDasRegistryURL() + dasRegistry
193               .getLocalSourceString()).hashCode();
194       sfetch = sf;
195       _initingFetcher = false;
196       initingThread = null;
197     }
198     return sfetch;
199   }
200
201   private IProgressIndicator progressIndicator;
202
203   private volatile boolean _isConstructing = false;
204
205   private List<AlignFrame> newAlframes = null;
206
207   public SequenceFetcher(IProgressIndicator guiIndic)
208   {
209     this(guiIndic, null, null);
210   }
211
212   public SequenceFetcher(IProgressIndicator guiIndic,
213           final String selectedDb, final String queryString)
214   {
215     this._isConstructing = true;
216     this.progressIndicator = guiIndic;
217     final SequenceFetcher us = this;
218     // launch initialiser thread
219     Thread sf = new Thread(new Runnable()
220     {
221
222       @Override
223       public void run()
224       {
225         if (getSequenceFetcherSingleton(progressIndicator) != null)
226         {
227           us.initGui(progressIndicator, selectedDb, queryString);
228           us._isConstructing = false;
229         }
230         else
231         {
232           javax.swing.SwingUtilities.invokeLater(new Runnable()
233           {
234             @Override
235             public void run()
236             {
237               JvOptionPane
238                       .showInternalMessageDialog(
239                               Desktop.desktop,
240                               MessageManager
241                                       .getString("warn.couldnt_create_sequence_fetcher_client"),
242                               MessageManager
243                                       .getString("label.couldnt_create_sequence_fetcher"),
244                               JvOptionPane.ERROR_MESSAGE);
245             }
246           });
247
248           // raise warning dialog
249         }
250       }
251     });
252     sf.start();
253   }
254
255   /**
256    * blocking call which creates a new sequence fetcher panel, configures it and
257    * presses the OK button with the given database and query.
258    * 
259    * @param database
260    * @param query
261    */
262   public static List<AlignFrame> fetchAndShow(String database, String query)
263   {
264     final SequenceFetcher sf = new SequenceFetcher(Desktop.instance,
265             database, query);
266     while (sf._isConstructing)
267     {
268       try
269       {
270         Thread.sleep(50);
271       } catch (Exception q)
272       {
273         return Collections.emptyList();
274       }
275     }
276     sf.newAlframes = new ArrayList<AlignFrame>();
277     sf.run();
278     return sf.newAlframes;
279   }
280
281   private class DatabaseAuthority extends DefaultMutableTreeNode
282   {
283
284   };
285
286   private class DatabaseSource extends DefaultMutableTreeNode
287   {
288
289   };
290
291   /**
292    * initialise the database and query for this fetcher panel
293    * 
294    * @param selectedDb
295    *          - string that should correspond to a sequence fetcher
296    * @param queryString
297    *          - string that will be entered in the query dialog
298    * @return true if UI was configured with valid database and query string
299    */
300   protected boolean setInitialQuery(String selectedDb, String queryString)
301   {
302     if (selectedDb == null || selectedDb.trim().length() == 0)
303     {
304       return false;
305     }
306     try
307     {
308       List<DbSourceProxy> sp = sfetch.getSourceProxy(selectedDb);
309       for (DbSourceProxy sourcep : sp)
310       {
311         if (sourcep.getTier() == 0)
312         {
313           database.selection = Arrays
314                   .asList(new DbSourceProxy[] { sourcep });
315           break;
316         }
317       }
318       if (database.selection == null || database.selection.size() == 0)
319       {
320         System.err.println("Ignoring fetch parameter db='" + selectedDb
321                 + "'");
322         return false;
323       }
324       textArea.setText(queryString);
325     } catch (Exception q)
326     {
327       System.err.println("Ignoring fetch parameter db='" + selectedDb
328               + "' and query='" + queryString + "'");
329       return false;
330     }
331     return true;
332   }
333
334   /**
335    * called by thread spawned by constructor
336    * 
337    * @param guiWindow
338    * @param queryString
339    * @param selectedDb
340    */
341   private void initGui(IProgressIndicator guiWindow, String selectedDb,
342           String queryString)
343   {
344     this.guiWindow = guiWindow;
345     if (guiWindow instanceof AlignFrame)
346     {
347       alignFrame = (AlignFrame) guiWindow;
348     }
349     database = new JDatabaseTree(sfetch);
350     try
351     {
352       jbInit();
353       /*
354        * configure the UI with any query parameters we were called with
355        */
356       if (!setInitialQuery(selectedDb, queryString))
357       {
358         /*
359          * none provided, so show the database chooser
360          */
361         database.waitForInput();
362       }
363     } catch (Exception ex)
364     {
365       ex.printStackTrace();
366     }
367
368     frame = new JInternalFrame();
369     frame.setContentPane(this);
370     if (Platform.isAMac())
371     {
372       Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 240);
373     }
374     else
375     {
376       Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 180);
377     }
378   }
379
380   private String getFrameTitle()
381   {
382     return ((alignFrame == null) ? MessageManager
383             .getString("label.new_sequence_fetcher") : MessageManager
384             .getString("label.additional_sequence_fetcher"));
385   }
386
387   private void jbInit() throws Exception
388   {
389     this.setLayout(borderLayout2);
390
391     database.setFont(JvSwingUtils.getLabelFont());
392     dbeg.setFont(new java.awt.Font("Verdana", Font.BOLD, 11));
393     jLabel1.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
394     jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
395     jLabel1.setText(MessageManager
396             .getString("label.separate_multiple_accession_ids"));
397
398     replacePunctuation.setHorizontalAlignment(SwingConstants.CENTER);
399     replacePunctuation
400             .setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
401     replacePunctuation.setText(MessageManager
402             .getString("label.replace_commas_semicolons"));
403     ok.setText(MessageManager.getString("action.ok"));
404     ok.addActionListener(new ActionListener()
405     {
406       @Override
407       public void actionPerformed(ActionEvent e)
408       {
409         ok_actionPerformed();
410       }
411     });
412     clear.setText(MessageManager.getString("action.clear"));
413     clear.addActionListener(new ActionListener()
414     {
415       @Override
416       public void actionPerformed(ActionEvent e)
417       {
418         clear_actionPerformed();
419       }
420     });
421
422     example.setText(MessageManager.getString("label.example"));
423     example.addActionListener(new ActionListener()
424     {
425       @Override
426       public void actionPerformed(ActionEvent e)
427       {
428         example_actionPerformed();
429       }
430     });
431     close.setText(MessageManager.getString("action.close"));
432     close.addActionListener(new ActionListener()
433     {
434       @Override
435       public void actionPerformed(ActionEvent e)
436       {
437         close_actionPerformed(e);
438       }
439     });
440     textArea.setFont(JvSwingUtils.getLabelFont());
441     textArea.setLineWrap(true);
442     textArea.addKeyListener(new KeyAdapter()
443     {
444       @Override
445       public void keyPressed(KeyEvent e)
446       {
447         if (e.getKeyCode() == KeyEvent.VK_ENTER)
448         {
449           ok_actionPerformed();
450         }
451       }
452     });
453     jPanel3.setLayout(borderLayout1);
454     borderLayout1.setVgap(5);
455     jPanel1.add(ok);
456     jPanel1.add(example);
457     jPanel1.add(clear);
458     jPanel1.add(close);
459     jPanel2.setLayout(borderLayout3);
460     databaseButt = /*database.getDatabaseSelectorButton();
461                    final JButton viewdbs =*/new JButton(
462             MessageManager.getString("action.select_ddbb"));
463     databaseButt.addActionListener(new ActionListener()
464     {
465
466       @Override
467       public void actionPerformed(ActionEvent arg0)
468       {
469         hidePanel();
470         database.showDialog();
471       }
472     });
473     databaseButt.setFont(JvSwingUtils.getLabelFont());
474     database.addActionListener(new ActionListener()
475     {
476       @Override
477       public void actionPerformed(ActionEvent e)
478       {
479         String currentSelection = database.getSelectedItem();
480         if (currentSelection == null)
481         {
482           close_actionPerformed(null);
483         }
484
485         showPanel();
486
487         if ("pdb".equalsIgnoreCase(currentSelection))
488         {
489           pdbSourceAction();
490         }
491         else if ("uniprot".equalsIgnoreCase(currentSelection))
492         {
493           uniprotSourceAction();
494         }
495         else
496         {
497           otherSourceAction();
498         }
499         database.action = -1;
500       }
501     });
502
503     dbeg.setText("");
504     jPanel2.add(databaseButt, java.awt.BorderLayout.NORTH);
505     jPanel2.add(dbeg, java.awt.BorderLayout.CENTER);
506     JPanel jPanel2a = new JPanel(new BorderLayout());
507     jPanel2a.add(jLabel1, java.awt.BorderLayout.NORTH);
508     jPanel2a.add(replacePunctuation, java.awt.BorderLayout.SOUTH);
509     jPanel2.add(jPanel2a, java.awt.BorderLayout.SOUTH);
510     // jPanel2.setPreferredSize(new Dimension())
511     jPanel3.add(jScrollPane1, java.awt.BorderLayout.CENTER);
512     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
513     this.add(jPanel3, java.awt.BorderLayout.CENTER);
514     this.add(jPanel2, java.awt.BorderLayout.NORTH);
515     jScrollPane1.getViewport().add(textArea);
516   }
517
518   private void pdbSourceAction()
519   {
520     databaseButt.setText(database.getSelectedItem());
521     new PDBFTSPanel(this);
522     frame.dispose();
523   }
524
525   private void uniprotSourceAction()
526   {
527     databaseButt.setText(database.getSelectedItem());
528     new UniprotFTSPanel(this);
529     frame.dispose();
530   }
531
532   private void otherSourceAction()
533   {
534     try
535     {
536       databaseButt.setText(database.getSelectedItem()
537               + (database.getSelectedSources().size() > 1 ? " (and "
538                       + database.getSelectedSources().size() + " others)"
539                       : ""));
540       String eq = database.getExampleQueries();
541       dbeg.setText(MessageManager.formatMessage(
542               "label.example_query_param", new String[] { eq }));
543       boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
544       for (DbSourceProxy dbs : database.getSelectedSources())
545       {
546         if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
547         {
548           enablePunct = false;
549           break;
550         }
551       }
552       replacePunctuation.setEnabled(enablePunct);
553
554     } catch (Exception ex)
555     {
556       dbeg.setText("");
557       replacePunctuation.setEnabled(true);
558     }
559     jPanel2.repaint();
560   }
561
562   protected void example_actionPerformed()
563   {
564     DbSourceProxy db = null;
565     try
566     {
567       textArea.setText(database.getExampleQueries());
568     } catch (Exception ex)
569     {
570     }
571     jPanel3.repaint();
572   }
573
574   protected void clear_actionPerformed()
575   {
576     textArea.setText("");
577     jPanel3.repaint();
578   }
579
580   public void close_actionPerformed(ActionEvent e)
581   {
582     try
583     {
584       frame.setClosed(true);
585     } catch (Exception ex)
586     {
587     }
588   }
589
590   public void ok_actionPerformed()
591   {
592     databaseButt.setEnabled(false);
593     example.setEnabled(false);
594     textArea.setEnabled(false);
595     ok.setEnabled(false);
596     close.setEnabled(false);
597
598     Thread worker = new Thread(this);
599     worker.start();
600   }
601
602   private void resetDialog()
603   {
604     databaseButt.setEnabled(true);
605     example.setEnabled(true);
606     textArea.setEnabled(true);
607     ok.setEnabled(true);
608     close.setEnabled(true);
609   }
610
611   @Override
612   public void run()
613   {
614     String error = "";
615     if (!database.hasSelection())
616     {
617       error += "Please select the source database\n";
618     }
619     // TODO: make this transformation more configurable
620     com.stevesoft.pat.Regex empty;
621     if (replacePunctuation.isEnabled() && replacePunctuation.isSelected())
622     {
623       empty = new com.stevesoft.pat.Regex(
624       // replace commas and spaces with a semicolon
625               "(\\s|[,; ])+", ";");
626     }
627     else
628     {
629       // just turn spaces and semicolons into single semicolons
630       empty = new com.stevesoft.pat.Regex("(\\s|[; ])+", ";");
631     }
632     textArea.setText(empty.replaceAll(textArea.getText()));
633     // see if there's anthing to search with
634     if (!new com.stevesoft.pat.Regex("[A-Za-z0-9_.]").search(textArea
635             .getText()))
636     {
637       error += "Please enter a (semi-colon separated list of) database id(s)";
638     }
639     if (error.length() > 0)
640     {
641       showErrorMessage(error);
642       resetDialog();
643       return;
644     }
645     // TODO: Refactor to GUI independent code and write tests.
646     // indicate if successive sources should be merged into one alignment.
647     boolean addToLast = false;
648     List<String> aresultq = new ArrayList<String>();
649     List<String> presultTitle = new ArrayList<String>();
650     List<AlignmentI> presult = new ArrayList<AlignmentI>();
651     List<AlignmentI> aresult = new ArrayList<AlignmentI>();
652     Iterator<DbSourceProxy> proxies = database.getSelectedSources()
653             .iterator();
654     String[] qries;
655     List<String> nextFetch = Arrays.asList(qries = textArea.getText()
656             .split(";"));
657     Iterator<String> en = Arrays.asList(new String[0]).iterator();
658     int nqueries = qries.length;
659
660     FeatureSettingsModelI preferredFeatureColours = null;
661     while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
662     {
663       if (!en.hasNext() && nextFetch.size() > 0)
664       {
665         en = nextFetch.iterator();
666         nqueries = nextFetch.size();
667         // save the remaining queries in the original array
668         qries = nextFetch.toArray(new String[nqueries]);
669         nextFetch = new ArrayList<String>();
670       }
671
672       DbSourceProxy proxy = proxies.next();
673       try
674       {
675         // update status
676         guiWindow
677                 .setProgressBar(MessageManager.formatMessage(
678                         "status.fetching_sequence_queries_from",
679                         new String[] {
680                             Integer.valueOf(nqueries).toString(),
681                             proxy.getDbName() }), Thread.currentThread()
682                         .hashCode());
683         if (proxy.getMaximumQueryCount() == 1)
684         {
685           /*
686            * proxy only handles one accession id at a time
687            */
688           while (en.hasNext())
689           {
690             String acc = en.next();
691             if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
692             {
693               nextFetch.add(acc);
694             }
695           }
696         }
697         else
698         {
699           /*
700            * proxy can fetch multiple accessions at one time
701            */
702           fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
703         }
704       } catch (Exception e)
705       {
706         showErrorMessage("Error retrieving " + textArea.getText()
707                 + " from " + database.getSelectedItem());
708         // error
709         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
710         System.err.println("Retrieval failed for source ='"
711                 + database.getSelectedItem() + "' and query\n'"
712                 + textArea.getText() + "'\n");
713         e.printStackTrace();
714       } catch (OutOfMemoryError e)
715       {
716         showErrorMessage("Out of Memory when retrieving "
717                 + textArea.getText()
718                 + " from "
719                 + database.getSelectedItem()
720                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
721         e.printStackTrace();
722       } catch (Error e)
723       {
724         showErrorMessage("Serious Error retrieving " + textArea.getText()
725                 + " from " + database.getSelectedItem());
726         e.printStackTrace();
727       }
728
729       // Stack results ready for opening in alignment windows
730       if (aresult != null && aresult.size() > 0)
731       {
732         FeatureSettingsModelI proxyColourScheme = proxy
733                 .getFeatureColourScheme();
734         if (proxyColourScheme != null)
735         {
736           preferredFeatureColours = proxyColourScheme;
737         }
738
739         AlignmentI ar = null;
740         if (proxy.isAlignmentSource())
741         {
742           addToLast = false;
743           // new window for each result
744           while (aresult.size() > 0)
745           {
746             presult.add(aresult.remove(0));
747             presultTitle.add(aresultq.remove(0) + " "
748                     + getDefaultRetrievalTitle());
749           }
750         }
751         else
752         {
753           String titl = null;
754           if (addToLast && presult.size() > 0)
755           {
756             ar = presult.remove(presult.size() - 1);
757             titl = presultTitle.remove(presultTitle.size() - 1);
758           }
759           // concatenate all results in one window
760           while (aresult.size() > 0)
761           {
762             if (ar == null)
763             {
764               ar = aresult.remove(0);
765             }
766             else
767             {
768               ar.append(aresult.remove(0));
769             }
770           }
771           addToLast = true;
772           presult.add(ar);
773           presultTitle.add(titl);
774         }
775       }
776       guiWindow.setProgressBar(MessageManager
777               .getString("status.finshed_querying"), Thread.currentThread()
778               .hashCode());
779     }
780     guiWindow.setProgressBar(
781             (presult.size() > 0) ? MessageManager
782                     .getString("status.parsing_results") : MessageManager
783                     .getString("status.processing"), Thread.currentThread()
784                     .hashCode());
785     // process results
786     while (presult.size() > 0)
787     {
788       parseResult(presult.remove(0), presultTitle.remove(0), null,
789               preferredFeatureColours);
790     }
791     // only remove visual delay after we finished parsing.
792     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
793     if (nextFetch.size() > 0)
794     {
795       StringBuffer sb = new StringBuffer();
796       sb.append("Didn't retrieve the following "
797               + (nextFetch.size() == 1 ? "query" : nextFetch.size()
798                       + " queries") + ": \n");
799       int l = sb.length(), lr = 0;
800       for (String s : nextFetch)
801       {
802         if (l != sb.length())
803         {
804           sb.append("; ");
805         }
806         if (lr - sb.length() > 40)
807         {
808           sb.append("\n");
809         }
810         sb.append(s);
811       }
812       showErrorMessage(sb.toString());
813     }
814     resetDialog();
815   }
816
817   /**
818    * Tries to fetch one or more accession ids from the database proxy
819    * 
820    * @param proxy
821    * @param accessions
822    *          the queries to fetch
823    * @param aresultq
824    *          a successful queries list to add to
825    * @param aresult
826    *          a list of retrieved alignments to add to
827    * @param nextFetch
828    *          failed queries are added to this list
829    * @throws Exception
830    */
831   void fetchMultipleAccessions(DbSourceProxy proxy,
832           Iterator<String> accessions, List<String> aresultq,
833           List<AlignmentI> aresult, List<String> nextFetch)
834           throws Exception
835   {
836     StringBuilder multiacc = new StringBuilder();
837     List<String> tosend = new ArrayList<String>();
838     while (accessions.hasNext())
839     {
840       String nel = accessions.next();
841       tosend.add(nel);
842       multiacc.append(nel);
843       if (accessions.hasNext())
844       {
845         multiacc.append(proxy.getAccessionSeparator());
846       }
847     }
848
849     try
850     {
851       String query = multiacc.toString();
852       AlignmentI rslt = proxy.getSequenceRecords(query);
853       if (rslt == null || rslt.getHeight() == 0)
854       {
855         // no results - pass on all queries to next source
856         nextFetch.addAll(tosend);
857       }
858       else
859       {
860         aresultq.add(query);
861         aresult.add(rslt);
862         if (tosend.size() > 1)
863         {
864           checkResultForQueries(rslt, tosend, nextFetch, proxy);
865         }
866       }
867     } catch (OutOfMemoryError oome)
868     {
869       new OOMWarning("fetching " + multiacc + " from "
870               + database.getSelectedItem(), oome, this);
871     }
872   }
873
874   /**
875    * Query for a single accession id via the database proxy
876    * 
877    * @param proxy
878    * @param accession
879    * @param aresultq
880    *          a list of successful queries to add to
881    * @param aresult
882    *          a list of retrieved alignments to add to
883    * @return true if the fetch was successful, else false
884    */
885   boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
886           List<String> aresultq, List<AlignmentI> aresult)
887   {
888     boolean success = false;
889     try
890     {
891       if (aresult != null)
892       {
893         try
894         {
895           // give the server a chance to breathe
896           Thread.sleep(5);
897         } catch (Exception e)
898         {
899           //
900         }
901       }
902
903       AlignmentI indres = null;
904       try
905       {
906         indres = proxy.getSequenceRecords(accession);
907       } catch (OutOfMemoryError oome)
908       {
909         new OOMWarning("fetching " + accession + " from "
910                 + proxy.getDbName(), oome, this);
911       }
912       if (indres != null)
913       {
914         aresultq.add(accession);
915         aresult.add(indres);
916         success = true;
917       }
918     } catch (Exception e)
919     {
920       Cache.log.info(
921               "Error retrieving " + accession + " from "
922                       + proxy.getDbName(), e);
923     }
924     return success;
925   }
926
927   /**
928    * Checks which of the queries were successfully retrieved by searching the
929    * DBRefs of the retrieved sequences for a match. Any not found are added to
930    * the 'nextFetch' list.
931    * 
932    * @param rslt
933    * @param queries
934    * @param nextFetch
935    * @param proxy
936    */
937   void checkResultForQueries(AlignmentI rslt, List<String> queries,
938           List<String> nextFetch, DbSourceProxy proxy)
939   {
940     SequenceI[] rs = rslt.getSequencesArray();
941
942     for (String q : queries)
943     {
944       DBRefEntry dbr = new DBRefEntry();
945       dbr.setSource(proxy.getDbSource());
946       dbr.setVersion(null);
947       String accId = proxy.getAccessionIdFromQuery(q);
948       dbr.setAccessionId(accId);
949       boolean rfound = false;
950       for (int r = 0; r < rs.length; r++)
951       {
952         if (rs[r] != null)
953         {
954           List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
955                   accId);
956           if (!found.isEmpty())
957           {
958             rfound = true;
959             break;
960           }
961         }
962       }
963       if (!rfound)
964       {
965         nextFetch.add(q);
966       }
967     }
968   }
969
970   /**
971    * 
972    * @return a standard title for any results retrieved using the currently
973    *         selected source and settings
974    */
975   public String getDefaultRetrievalTitle()
976   {
977     return "Retrieved from " + database.getSelectedItem();
978   }
979
980   AlignmentI parseResult(AlignmentI al, String title,
981           FileFormatI currentFileFormat,
982           FeatureSettingsModelI preferredFeatureColours)
983   {
984
985     if (al != null && al.getHeight() > 0)
986     {
987       if (title == null)
988       {
989         title = getDefaultRetrievalTitle();
990       }
991       if (alignFrame == null)
992       {
993         AlignFrame af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
994                 AlignFrame.DEFAULT_HEIGHT);
995         if (currentFileFormat != null)
996         {
997           af.currentFileFormat = currentFileFormat; // WHAT IS THE DEFAULT
998           // FORMAT FOR
999           // NON-FormatAdapter Sourced
1000           // Alignments?
1001         }
1002
1003         SequenceFeature[] sfs = null;
1004         List<SequenceI> alsqs;
1005         synchronized (alsqs = al.getSequences())
1006         {
1007           for (SequenceI sq : alsqs)
1008           {
1009             if ((sfs = sq.getSequenceFeatures()) != null)
1010             {
1011               if (sfs.length > 0)
1012               {
1013                 af.setShowSeqFeatures(true);
1014                 break;
1015               }
1016             }
1017
1018           }
1019         }
1020
1021         if (preferredFeatureColours != null)
1022         {
1023           af.getViewport().applyFeaturesStyle(preferredFeatureColours);
1024         }
1025         if (Cache.getDefault("HIDE_INTRONS", true))
1026         {
1027           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
1028         }
1029         if (newAlframes != null)
1030         {
1031           newAlframes.add(af);
1032         }
1033         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
1034                 AlignFrame.DEFAULT_HEIGHT);
1035
1036         af.statusBar.setText(MessageManager
1037                 .getString("label.successfully_pasted_alignment_file"));
1038
1039         try
1040         {
1041           af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
1042         } catch (Exception ex)
1043         {
1044         }
1045       }
1046       else
1047       {
1048         alignFrame.viewport.addAlignment(al, title);
1049       }
1050     }
1051     return al;
1052   }
1053
1054   void showErrorMessage(final String error)
1055   {
1056     resetDialog();
1057     javax.swing.SwingUtilities.invokeLater(new Runnable()
1058     {
1059       @Override
1060       public void run()
1061       {
1062         JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
1063                 MessageManager.getString("label.error_retrieving_data"),
1064                 JvOptionPane.WARNING_MESSAGE);
1065       }
1066     });
1067   }
1068
1069   public IProgressIndicator getProgressIndicator()
1070   {
1071     return progressIndicator;
1072   }
1073
1074   public void setProgressIndicator(IProgressIndicator progressIndicator)
1075   {
1076     this.progressIndicator = progressIndicator;
1077   }
1078
1079   /**
1080    * Make this panel visible (after a selection has been made in the database
1081    * chooser)
1082    */
1083   void showPanel()
1084   {
1085     frame.setVisible(true);
1086   }
1087
1088   /**
1089    * Hide this panel (on clicking the database button to open the database
1090    * chooser)
1091    */
1092   void hidePanel()
1093   {
1094     frame.setVisible(false);
1095   }
1096 }