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