JAL-2871 JAL-2877 third time's the charm?
[jalview.git] / src / jalview / ext / archaeopteryx / AptxInit.java
1 package jalview.ext.archaeopteryx;
2
3 import jalview.analysis.TreeBuilder;
4 import jalview.bin.Cache;
5 import jalview.datamodel.SequenceI;
6 import jalview.ext.treeviewer.ExternalTreeBuilderI;
7 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
8 import jalview.gui.Desktop;
9 import jalview.gui.JvOptionPane;
10 import jalview.util.MessageManager;
11 import jalview.viewmodel.AlignmentViewport;
12
13 import java.awt.Component;
14 import java.awt.Dimension;
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import javax.swing.JMenu;
24 import javax.swing.JMenuBar;
25 import javax.swing.JMenuItem;
26 import javax.swing.JSeparator;
27
28 import org.forester.archaeopteryx.AptxUtil;
29 import org.forester.archaeopteryx.Archaeopteryx;
30 import org.forester.archaeopteryx.Configuration;
31 import org.forester.archaeopteryx.MainFrame;
32 import org.forester.archaeopteryx.webservices.PhylogeniesWebserviceClient;
33 import org.forester.archaeopteryx.webservices.WebserviceUtil;
34 import org.forester.io.parsers.PhylogenyParser;
35 import org.forester.io.parsers.nexus.NexusPhylogeniesParser;
36 import org.forester.io.parsers.nhx.NHXParser;
37 import org.forester.io.parsers.phyloxml.PhyloXmlDataFormatException;
38 import org.forester.io.parsers.phyloxml.PhyloXmlParser;
39 import org.forester.io.parsers.tol.TolParser;
40 import org.forester.io.parsers.util.ParserUtils;
41 import org.forester.phylogeny.Phylogeny;
42 import org.forester.phylogeny.PhylogenyMethods;
43 import org.forester.phylogeny.PhylogenyNode;
44 import org.forester.phylogeny.data.Identifier;
45 import org.forester.util.ForesterUtil;
46
47 /**
48  * Static class for creating Archaeopteryx tree viewer instances from calculated
49  * trees and letting them be bound to Jalview.
50  * 
51  * @author kjvanderheide
52  *
53  */
54 public final class AptxInit
55 {
56   // yet to add user preferred config
57   private final static Configuration APTX_CONFIG = new Configuration(
58           Cache.getDefault("APTXCONFIG",
59                   Desktop.instance.getClass()
60                           .getResource(
61                                   "/_aptx_jalview_configuration_file.txt")
62                           .toString()),
63             false, false, false);
64
65   private final static boolean VALIDATE_PHYLOXML_XSD = APTX_CONFIG
66           .isValidatePhyloXmlAgainstSchema();
67
68   private final static boolean REPLACE_NHX_UNDERSCORES = APTX_CONFIG
69           .isReplaceUnderscoresInNhParsing();
70
71   private final static boolean INTERNAL_NUMBERS_AS_CONFIDENCE = APTX_CONFIG
72           .isInternalNumberAreConfidenceForNhParsing();
73
74   private final static boolean MIDPOINT_REROOT = APTX_CONFIG
75           .isMidpointReroot();
76
77   private final static NHXParser.TAXONOMY_EXTRACTION TAXONOMY_EXTRACTION = APTX_CONFIG
78           .getTaxonomyExtraction();
79
80   private static Map<MainFrame, JalviewBinding> activeAptx = new HashMap<>();
81
82
83
84   public static MainFrame createInstanceFromCalculation(
85           final TreeBuilder calculatedTree)
86   {
87     ExternalTreeBuilderI<Phylogeny, PhylogenyNode> aptxTreeBuilder = new AptxTreeBuilder(
88             calculatedTree);
89
90     Phylogeny aptxTree = aptxTreeBuilder.buildTree();
91
92     MainFrame aptxApp = createAptxFrame(aptxTree,
93             calculatedTree.getAvport(), null);
94             
95     return aptxApp;
96   }
97
98   /**
99    * Refactored from Archaeopteryx.main
100    * 
101    * @param filePath
102    * @param viewport
103    * @return
104    * @throws IOException
105    * @throws FileNotFoundException
106    */
107   public static MainFrame[] createInstancesFromFile(String filePath,
108           AlignmentViewport viewport)
109           throws FileNotFoundException, IOException
110   {
111     File treeFile = new File(filePath);
112     final String err = ForesterUtil.isReadableFile(treeFile);
113     if (!ForesterUtil.isEmpty(err))
114     {
115       JvOptionPane.showMessageDialog(Desktop.desktop, err,
116               MessageManager.getString("label.problem_reading_tree_file"),
117               JvOptionPane.WARNING_MESSAGE);
118     }
119
120     if (Desktop.instance != null)
121     {
122       Desktop.instance.startLoading(filePath);
123     }
124     boolean nhx_or_nexus = false;
125     final PhylogenyParser parser = ParserUtils.createParserDependingOnFileType(
126             treeFile, VALIDATE_PHYLOXML_XSD);
127     if (parser instanceof NHXParser)
128     {
129       nhx_or_nexus = true;
130       final NHXParser nhx = (NHXParser) parser;
131       nhx.setReplaceUnderscores(REPLACE_NHX_UNDERSCORES);
132       nhx.setIgnoreQuotes(false);
133       nhx.setTaxonomyExtraction(TAXONOMY_EXTRACTION);
134     }
135     else if (parser instanceof NexusPhylogeniesParser)
136     {
137       nhx_or_nexus = true;
138       final NexusPhylogeniesParser nex = (NexusPhylogeniesParser) parser;
139       nex.setReplaceUnderscores(REPLACE_NHX_UNDERSCORES);
140       nex.setIgnoreQuotes(false);
141     }
142     else if (parser instanceof PhyloXmlParser)
143     {
144       if (VALIDATE_PHYLOXML_XSD == false)
145       {
146         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
147                 MessageManager.getString("error.phyloxml_validation"),
148                 MessageManager.getString("label.file_open_error"),
149                                        JvOptionPane.WARNING_MESSAGE );
150       }
151     }
152     Phylogeny[] trees = PhylogenyMethods.readPhylogenies(parser, treeFile);
153     MainFrame[] aptxFrames = new MainFrame[trees.length];
154
155
156     for (int i = 0; i < trees.length; i++)
157       {
158       Phylogeny tree = trees[i];
159
160       if (nhx_or_nexus && INTERNAL_NUMBERS_AS_CONFIDENCE)
161       {
162         PhylogenyMethods.transferInternalNodeNamesToConfidence(tree, "");
163       }
164       String treeTitle = treeFile.getName() + "[" + i + "]";
165       tree.setName(treeTitle);
166       aptxFrames[i] = createAptxFrame(tree, viewport, treeTitle);
167
168     }
169     if (Desktop.instance != null)
170     {
171       Desktop.instance.stopLoading();
172     }
173     return aptxFrames;
174     }
175
176
177   public static MainFrame[] createInstancesFromUrl(URL treeUrl,
178           AlignmentViewport viewport)
179           throws FileNotFoundException, IOException, RuntimeException
180   {
181     
182     String treeTitle = treeUrl.getFile();
183     if (Desktop.instance != null)
184     {
185       Desktop.instance.startLoading(treeTitle);
186     }
187     Phylogeny[] trees = AptxUtil.readPhylogeniesFromUrl(treeUrl,
188             VALIDATE_PHYLOXML_XSD,
189              REPLACE_NHX_UNDERSCORES, INTERNAL_NUMBERS_AS_CONFIDENCE,
190             TAXONOMY_EXTRACTION, MIDPOINT_REROOT);
191
192     MainFrame[] aptxFrames = new MainFrame[trees.length];
193     for (int i = 0; i < trees.length; i++)
194     {
195       Phylogeny tree = trees[i];
196       aptxFrames[i] = createAptxFrame(tree, viewport, treeTitle);
197     }
198
199     if (Desktop.instance != null)
200     {
201       Desktop.instance.stopLoading();
202     }
203
204     return aptxFrames;
205
206   }
207
208   /**
209    * Refactored from Forester's UrlTreeReader, this can be more efficient
210    * 
211    * @param databaseIndex
212    * @param viewport
213    * @return
214    */
215   public static MainFrame[] createInstancesFromDb(
216           PhylogeniesWebserviceClient treeDbClient, String identifier,
217           AlignmentViewport viewport)
218   {
219
220     URL url = null;
221     Phylogeny[] trees = null;
222
223     if ((identifier != null) && (identifier.trim().length() > 0))
224     {
225       if (Desktop.instance != null)
226       {
227         Desktop.instance.startLoading(identifier);
228       }
229
230       identifier = identifier.trim();
231       if (treeDbClient.isQueryInteger())
232       {
233         identifier = identifier.replaceAll("^\\D+", "");
234
235         int id;
236         try
237         {
238           id = Integer.parseInt(identifier);
239         } catch (final NumberFormatException e)
240         {
241           JvOptionPane.showInternalMessageDialog(Desktop.desktop,
242                   MessageManager.formatMessage(
243                           "error.database_id_has_letters", new String[]
244                           { identifier }),
245                   MessageManager.getString("label.invalid_url"),
246                   JvOptionPane.ERROR_MESSAGE);
247           return new MainFrame[0];
248         }
249         identifier = id + "";
250       }
251       boolean exception = false;
252       try
253       {
254         String url_str = treeDbClient.getUrl();
255         url_str = url_str.replaceFirst(
256                 PhylogeniesWebserviceClient.QUERY_PLACEHOLDER, identifier);
257         url = new URL(url_str);
258         PhylogenyParser parser = null;
259         switch (treeDbClient.getReturnFormat())
260         {
261         case TOL_XML_RESPONSE:
262           parser = new TolParser();
263           break;
264         case NEXUS:
265           parser = new NexusPhylogeniesParser();
266           ((NexusPhylogeniesParser) parser).setReplaceUnderscores(true);
267           break;
268         case TREEBASE_TREE:
269           parser = new NexusPhylogeniesParser();
270           ((NexusPhylogeniesParser) parser).setReplaceUnderscores(true);
271           ((NexusPhylogeniesParser) parser)
272                   .setTaxonomyExtraction(NHXParser.TAXONOMY_EXTRACTION.NO);
273           break;
274         case TREEBASE_STUDY:
275           parser = new NexusPhylogeniesParser();
276           ((NexusPhylogeniesParser) parser).setReplaceUnderscores(true);
277           ((NexusPhylogeniesParser) parser)
278                   .setTaxonomyExtraction(NHXParser.TAXONOMY_EXTRACTION.NO);
279           break;
280         case NH:
281           parser = new NHXParser();
282           ((NHXParser) parser)
283                   .setTaxonomyExtraction(NHXParser.TAXONOMY_EXTRACTION.NO);
284           ((NHXParser) parser).setReplaceUnderscores(true);
285           ((NHXParser) parser).setGuessRootedness(true);
286           break;
287         case NH_EXTRACT_TAXONOMY:
288           parser = new NHXParser();
289           ((NHXParser) parser).setTaxonomyExtraction(
290                   NHXParser.TAXONOMY_EXTRACTION.AGGRESSIVE);
291           ((NHXParser) parser).setReplaceUnderscores(false);
292           ((NHXParser) parser).setGuessRootedness(true);
293           break;
294         case PFAM:
295           parser = new NHXParser();
296           ((NHXParser) parser).setTaxonomyExtraction(
297                   NHXParser.TAXONOMY_EXTRACTION.PFAM_STYLE_STRICT);
298           ((NHXParser) parser).setReplaceUnderscores(false);
299           ((NHXParser) parser).setGuessRootedness(true);
300           break;
301         case NHX:
302           parser = new NHXParser();
303           ((NHXParser) parser)
304                   .setTaxonomyExtraction(NHXParser.TAXONOMY_EXTRACTION.NO);
305           ((NHXParser) parser).setReplaceUnderscores(false);
306           ((NHXParser) parser).setGuessRootedness(true);
307           break;
308         case PHYLOXML:
309           parser = PhyloXmlParser.createPhyloXmlParserXsdValidating();
310           break;
311         default:
312           throw new IllegalArgumentException(
313                   "unknown format: " + treeDbClient.getReturnFormat());
314         }
315         //
316         // if (_main_frame.getMainPanel().getCurrentTreePanel() != null)
317         // {
318         // _main_frame.getMainPanel().getCurrentTreePanel().setWaitCursor();
319         // }
320         // else
321         // {
322         // _main_frame.getMainPanel().setWaitCursor();
323         // }
324         trees = ForesterUtil.readPhylogeniesFromUrl(url, parser);
325       } catch (final MalformedURLException e)
326       {
327         exception = true;
328         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
329                 MessageManager.formatMessage(
330                         "exception.unable_to_launch_url", new String[]
331                         { url.toString() }),
332                 MessageManager.getString("label.invalid_url"),
333                 JvOptionPane.ERROR_MESSAGE);
334         System.err.println(e.getLocalizedMessage());
335       } catch (final IOException e)
336       {
337         exception = true;
338         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
339                 "Could not read from " + url + "\n"
340                         + e.getLocalizedMessage(),
341                 "Failed to read tree from " + treeDbClient.getName() + " for "
342                         + identifier,
343                 JvOptionPane.ERROR_MESSAGE);
344         System.err.println(e.getLocalizedMessage());
345       } catch (final NumberFormatException e)
346       {
347         exception = true;
348         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
349                 "Could not read from " + url + "\n"
350                         + e.getLocalizedMessage(),
351                 "Failed to read tree from " + treeDbClient.getName() + " for "
352                         + identifier,
353                 JvOptionPane.ERROR_MESSAGE);
354         System.err.println(e.getLocalizedMessage());
355       } catch (final Exception e)
356       {
357         exception = true;
358         e.printStackTrace();
359         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
360                 e.getLocalizedMessage(), "Unexpected Exception",
361                 JvOptionPane.ERROR_MESSAGE);
362         System.err.println(e.getLocalizedMessage());
363       }
364       if ((trees != null) && (trees.length > 0))
365       {
366         for (final Phylogeny phylogeny : trees)
367         {
368           if (!phylogeny.isEmpty())
369           {
370             if (treeDbClient.getName().equals(WebserviceUtil.TREE_FAM_NAME))
371             {
372               phylogeny.setRerootable(false);
373               phylogeny.setRooted(true);
374             }
375             if (treeDbClient.getProcessingInstructions() != null)
376             {
377               try
378               {
379                 WebserviceUtil.processInstructions(treeDbClient, phylogeny);
380               } catch (final PhyloXmlDataFormatException e)
381               {
382                 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
383                         "Error:\n" + e.getLocalizedMessage(), "Error",
384                         JvOptionPane.ERROR_MESSAGE);
385               }
386             }
387             if (treeDbClient.getNodeField() != null)
388             {
389               try
390               {
391                 PhylogenyMethods.transferNodeNameToField(phylogeny,
392                         treeDbClient.getNodeField(), false);
393               } catch (final PhyloXmlDataFormatException e)
394               {
395                 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
396                         "Error:\n" + e.getLocalizedMessage(), "Error",
397                         JvOptionPane.ERROR_MESSAGE);
398               }
399             }
400             phylogeny.setIdentifier(
401                     new Identifier(identifier, treeDbClient.getName()));
402             // _main_frame.getJMenuBar().remove(_main_frame.getHelpMenu());
403             // _main_frame.getMenuBarOfMainFrame()
404             // .add(_main_frame.getHelpMenu());
405             // _main_frame.getMainPanel().addPhylogenyInNewTab(phylogeny,
406             // _main_frame.getConfiguration(),
407             // new File(url.getFile()).getName(), url.toString());
408
409             MainFrame aptxApp = createAptxFrame(phylogeny, viewport,
410                     url.getFile());
411             String my_name_for_file = "";
412             if (!ForesterUtil.isEmpty(phylogeny.getName()))
413             {
414               my_name_for_file = new String(phylogeny.getName())
415                       .replaceAll(" ", "_");
416             }
417             else if (phylogeny.getIdentifier() != null)
418             {
419               final StringBuffer sb = new StringBuffer();
420               if (!ForesterUtil
421                       .isEmpty(phylogeny.getIdentifier().getProvider()))
422               {
423                 sb.append(phylogeny.getIdentifier().getProvider());
424                 sb.append("_");
425               }
426               sb.append(phylogeny.getIdentifier().getValue());
427               my_name_for_file = new String(
428                       sb.toString().replaceAll(" ", "_"));
429             }
430             aptxApp.getMainPanel().getCurrentTreePanel()
431                     .setTreeFile(new File(my_name_for_file));
432             AptxUtil.lookAtSomeTreePropertiesForAptxControlSettings(
433                     phylogeny, aptxApp.getMainPanel().getControlPanel(),
434                     APTX_CONFIG);
435             // _main_frame.getMainPanel().getControlPanel().showWhole();
436
437             aptxApp.activateSaveAllIfNeeded();
438           }
439         }
440       }
441       else if (!exception) // ..what?
442       {
443         JvOptionPane.showMessageDialog(null,
444                 ForesterUtil.wordWrap(
445                         "Failed to read in tree(s) from [" + url + "]", 80),
446                 "Error", JvOptionPane.ERROR_MESSAGE);
447       }
448       if ((trees != null) && (trees.length > 0))
449       {
450         try
451         {
452           JvOptionPane.showMessageDialog(null,
453                   ForesterUtil.wordWrap("Successfully read in "
454                           + trees.length + " tree(s) from [" + url + "]",
455                           80),
456                   "Success", JvOptionPane.INFORMATION_MESSAGE);
457         } catch (final Exception e)
458         {
459           // Not important if this fails, do nothing.
460         }
461         // _main_frame.getContentPane().repaint();
462       }
463     }
464
465     System.gc();
466
467
468     if (Desktop.instance != null)
469     {
470       Desktop.instance.stopLoading();
471     }
472     return null;
473
474
475   }
476
477
478
479
480
481   public static MainFrame createAptxFrame(
482           final Phylogeny aptxTree,
483           final AlignmentViewport jalviewAlignport, String treeTitle)
484   {
485     if (APTX_CONFIG == null || APTX_CONFIG.isCouldReadConfigFile() == false)
486     {
487       int keepGoing = JvOptionPane.showConfirmDialog(Desktop.desktop,
488               MessageManager.getString("label.aptx_config_not_found"),
489               MessageManager.formatMessage("label.couldnt_locate",
490                       new String[]
491                       { "_aptx_jalview_configuration_file" }),
492               JvOptionPane.YES_NO_CANCEL_OPTION);
493
494       if (keepGoing == JvOptionPane.CANCEL_OPTION
495               || keepGoing == JvOptionPane.CLOSED_OPTION
496               || keepGoing == JvOptionPane.NO_OPTION)
497       {
498         return null;
499       }
500
501     }
502     MainFrame aptxApp = Archaeopteryx.createApplication(aptxTree,
503             APTX_CONFIG, treeTitle);
504
505
506     LoadedTreeSequenceAssociation bindAptxNodes = new LoadedTreeSequenceAssociation(
507             jalviewAlignport.getAlignment().getSequencesArray(), aptxTree);
508     bindAptxNodes.associateLeavesToSequences();
509
510     bindNodesToJalviewSequences(aptxApp, jalviewAlignport,
511             bindAptxNodes.getAlignmentWithNodes(),
512             bindAptxNodes.getNodesWithAlignment());
513     bindTreeViewFrameToJalview(aptxApp);
514
515     adaptAptxGui(aptxApp);
516     return aptxApp;
517   }
518
519
520   public static ExternalTreeViewerBindingI<?> bindNodesToJalviewSequences(
521           final MainFrame aptxApp,
522           final AlignmentViewport jalviewAlignViewport,
523           final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
524           final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
525   {
526     JalviewBinding treeBinding = new JalviewBinding(aptxApp,
527             jalviewAlignViewport,
528             alignMappedToNodes, nodesMappedToAlign);
529     activeAptx.put(aptxApp, treeBinding);
530     return treeBinding;
531   }
532
533
534   public static MainFrame bindTreeViewFrameToJalview(
535           final MainFrame aptxApp)
536   {
537     int width = 400;
538     int height = 550;
539     aptxApp.setMinimumSize(new Dimension(width, height));
540     // aptxApp.setFont(Desktop.instance.getFont());
541     // aptxApp.getMainPanel().setFont(Desktop.instance.getFont());
542     String frameTitle = MessageManager.getString("label.aptx_title");
543     File treeFile = aptxApp.getMainPanel().getCurrentTreePanel()
544             .getTreeFile();
545     if (treeFile != null)
546     {
547       frameTitle += MessageManager.formatMessage("label.aptx_title_append",
548               new String[]
549               { treeFile.getAbsolutePath() });
550     }
551     Desktop.addInternalFrame(aptxApp, frameTitle, true, width, height, true,
552             true);
553     return aptxApp;
554
555   }
556
557   /**
558    * Hides certain redundant Archaeopteryx GUI elements such as the menu items
559    * for reading in trees and adds extra items related to Jalview such as the
560    * tree sorting item.
561    * 
562    * 
563    * @param aptxFrame
564    */
565   private static void adaptAptxGui(MainFrame aptxFrame)
566   {
567     JMenuBar frameBar = aptxFrame.getJMenuBar();
568
569     for (int i = 0; i < frameBar.getMenuCount();i++) {
570       JMenu menu = frameBar.getMenu(i);
571
572       if (menu.getText().contains("File"))
573       {
574         // hide all "Read from ..." and "New" menu items and any Separators that
575         // come directly after them
576         Component previousMenuItem = null;
577         for (Component menuItem : menu.getMenuComponents()) {
578           if (previousMenuItem instanceof JMenuItem)
579           {
580             if (((JMenuItem) previousMenuItem).getText().startsWith("Read")
581                     || ((JMenuItem) previousMenuItem).getText()
582                             .startsWith("New"))
583             {
584               previousMenuItem.setVisible(false);
585
586               if (menuItem instanceof JSeparator)
587               {
588                 menuItem.setVisible(false);
589               }
590             }
591           }
592           previousMenuItem = menuItem;
593         }
594       }
595       else if (menu.getText().contains("Inference"))
596       {
597         menu.setVisible(false);
598       }
599       else if (menu.getText().contains("View"))
600       {
601         menu.addSeparator();
602         JMenuItem sortByTree = new JMenuItem("Sort alignment by tree");
603         JMenuItem refreshJalview = new JMenuItem(
604                 "Filter alignment to show only currently visible sequences");
605
606         refreshJalview.setFont(menu.getFont());
607
608         menu.add(sortByTree);
609         menu.add(refreshJalview);
610
611         sortByTree.setFont(menu.getFont());
612         sortByTree.setVisible(false); // don't show unless it's actually
613         // possible
614
615         refreshJalview.addActionListener(activeAptx.get(aptxFrame));
616
617
618       }
619
620     }
621     aptxFrame.validate();
622   }
623
624   public static Map<MainFrame, JalviewBinding> getAllAptxFrames()
625   {
626     return activeAptx;
627   }
628
629
630 }