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