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