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