/* * TreeSpaceFrame.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.app.treespace; import dr.app.gui.DeleteActionResponder; import dr.app.gui.SelectAllActionResponder; import dr.app.treespace.inputpanel.InputPanel; import dr.app.util.Utils; import dr.evolution.io.Importer; import dr.evolution.io.TreeImporter; import dr.evolution.tree.Tree; import jam.framework.DocumentFrame; import jam.framework.Exportable; import jam.util.IconUtils; import jebl.evolution.io.ImportException; import jebl.evolution.io.NexusImporter; import jebl.evolution.trees.RootedTree; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.plaf.BorderUIResource; import java.awt.*; import java.io.*; /** * @author Andrew Rambaut * @version $Id$ */ public class TreeSpaceFrame extends DocumentFrame { private static final long serialVersionUID = 2114148696789612509L; private final TreeSpaceDocument document = new TreeSpaceDocument(); private final JTabbedPane tabbedPane = new JTabbedPane(); private final JLabel statusLabel = new JLabel("No data loaded"); private final InputPanel inputPanel; private final CladePanel cladePanel; private final CladePlotter cladePlotter; private final TreePanel treePanel; private final TreePlotter treePlotter; private final JPanel plotterPanel; private JFileChooser importChooser; // make JFileChooser chooser remember previous path private JFileChooser exportChooser; // make JFileChooser chooser remember previous path private final Icon gearIcon = IconUtils.getIcon(this.getClass(), "images/gear.png"); private CladeSystem cladeSystem = new CladeSystem(); public TreeSpaceFrame(String title) { super(); setTitle(title); // Prevent the application to close in requestClose() // after a user cancel or a failure in beast file generation setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // getOpenAction().setEnabled(false); // getSaveAction().setEnabled(false); getFindAction().setEnabled(false); getZoomWindowAction().setEnabled(false); inputPanel = new InputPanel(this, document, getImportAction()); cladePanel = new CladePanel(this, document); treePanel = new TreePanel(this, document); cladePlotter = new CladePlotter(cladeSystem); treePlotter = new TreePlotter(); plotterPanel = new JPanel(new BorderLayout()); plotterPanel.add(new JScrollPane(treePlotter), BorderLayout.CENTER); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(final ChangeEvent e) { tabbedPaneChanged(); } }); document.addListener(new TreeSpaceDocument.Listener() { public void dataChanged() { setDirty(); } public void settingsChanged() { setDirty(); } }); } public void initializeComponents() { tabbedPane.addTab("Input", inputPanel); tabbedPane.addTab("Trees", treePanel); tabbedPane.addTab("Clades", cladePanel); tabbedPane.addTab("Graph", cladePlotter); tabbedPane.addTab("Plot", plotterPanel); JPanel panel = new JPanel(new BorderLayout(6, 6)); panel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12))); panel.add(tabbedPane, BorderLayout.CENTER); panel.setPreferredSize(new java.awt.Dimension(800, 600)); // getExportAction().setEnabled(false); // JButton generateButton = new JButton(getExportAction()); // generateButton.putClientProperty("JButton.buttonType", "roundRect"); JPanel panel2 = new JPanel(new BorderLayout(6, 6)); panel2.add(statusLabel, BorderLayout.NORTH); // panel2.add(generateButton, BorderLayout.EAST); panel.add(panel2, BorderLayout.SOUTH); getContentPane().setLayout(new java.awt.BorderLayout(0, 0)); getContentPane().add(panel, BorderLayout.CENTER); // setAllOptions(); setSize(new java.awt.Dimension(1024, 768)); setMinimumSize(new java.awt.Dimension(800, 600)); // make JFileChooser chooser remember previous path exportChooser = new JFileChooser(Utils.getCWD()); exportChooser.setDialogTitle("Generate File..."); importChooser = new JFileChooser(Utils.getCWD()); importChooser.setMultiSelectionEnabled(true); importChooser.setFileFilter(new FileNameExtensionFilter( "NEXUS (*.nex) & BEAST Tree (*.trees) Files", "nex", "nexus", "nx", "tree", "tre", "trees")); importChooser.setDialogTitle("Import Data Files..."); } public final void tabbedPaneChanged() { if (tabbedPane.getSelectedComponent() instanceof DeleteActionResponder) { getDeleteAction().setEnabled( ((DeleteActionResponder)(tabbedPane.getSelectedComponent())) .getDeleteAction() .isEnabled() ); } } public void doDelete() { if (tabbedPane.getSelectedComponent() instanceof DeleteActionResponder) { ((DeleteActionResponder)(tabbedPane.getSelectedComponent())).delete(); } } public void doSelectAll() { if (tabbedPane.getSelectedComponent() instanceof SelectAllActionResponder) { ((SelectAllActionResponder)(tabbedPane.getSelectedComponent())).selectAll(); } } public void setRemoveActionEnabled(final DeleteActionResponder responder, final boolean isEnabled) { if (responder == tabbedPane.getSelectedComponent()) { getDeleteAction().setEnabled(isEnabled); } } public boolean requestClose() { // if (isDirty()) { // int option = JOptionPane.showConfirmDialog(this, // "You have made changes but have not generated\n" + // "an output file. Do you wish to generate\n" + // "before closing this window?", // "Unused changes", // JOptionPane.YES_NO_CANCEL_OPTION, // JOptionPane.WARNING_MESSAGE); // // if (option == JOptionPane.YES_OPTION) { // return !doGenerate(); // } else if (option == JOptionPane.CANCEL_OPTION || option == JOptionPane.DEFAULT_OPTION) { // return false; // } // return true; // } return true; } protected boolean readFromFile(File file) throws IOException { return false; } public String getDefaultFileName() { return "untitled"; } protected boolean writeToFile(File file) throws IOException { return false; } public final void doImport() { int returnVal = importChooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File[] files = importChooser.getSelectedFiles(); for (File file : files) { if (file == null || file.getName().equals("")) { JOptionPane.showMessageDialog(this, "Invalid file name", "Invalid file name", JOptionPane.ERROR_MESSAGE); } else { try { importDataFile(file); setDirty(); // } catch (FileNotFoundException fnfe) { // JOptionPane.showMessageDialog(this, "Unable to open file: File not found", // "Unable to open file", JOptionPane.ERROR_MESSAGE); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "File I/O Error unable to read file: " + ioe.getMessage(), "Unable to read file", JOptionPane.ERROR_MESSAGE); return; } catch (NexusImporter.MissingBlockException ex) { JOptionPane.showMessageDialog(this, "TAXON, DATA or CHARACTERS block is missing in Nexus file: " + ex, "Missing Block in Nexus File", JOptionPane.ERROR_MESSAGE); } catch (ImportException ime) { JOptionPane.showMessageDialog(this, "Error parsing imported file: " + ime, "Error reading file", JOptionPane.ERROR_MESSAGE); return; // } catch (Exception ex) { // JOptionPane.showMessageDialog(this, "Fatal exception: " + ex, // "Error reading file", // JOptionPane.ERROR_MESSAGE); // return; } } } // setAllOptions(); } } public void importDataFile(File file) throws IOException, ImportException { Reader reader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(reader); String line = bufferedReader.readLine(); while (line != null && line.length() == 0) { line = bufferedReader.readLine(); } if ((line != null && line.toUpperCase().contains("#NEXUS"))) { int treeCount = 0; do { line = bufferedReader.readLine(); } while (line != null && !line.contains("(")); while (line != null) { if (line.contains("(")) { treeCount++; if (treeCount > 1) { break; } } line = bufferedReader.readLine(); } // is a NEXUS file RootedTree tree = importFirstTree(file); if (tree != null) { if (treeCount > 1) { InputFile inputFile = new InputFile(file, tree, -1); document.addTreeFile(inputFile); treePlotter.setTrees(loadTrees(inputFile)); inputFile.setTreeCount(treeCount); document.fireDataChanged(); } else { InputFile inputFile = new InputFile(file, tree); document.addTreeFile(inputFile); treePlotter.setTrees(loadTrees(inputFile)); inputFile.setTreeCount(treeCount); document.fireDataChanged(); } } } else { JOptionPane.showMessageDialog(this, "Error parsing imported file. This may not be a NEXUS file", "Error reading file", JOptionPane.ERROR_MESSAGE); } } // nexus private RootedTree importFirstTree(File file) throws IOException, ImportException { RootedTree tree = null; FileReader reader = new FileReader(file); NexusImporter importer = new NexusImporter(reader); tree = (RootedTree)importer.importNextTree(); return tree; } private TreeLineages loadTrees(InputFile inputFile) throws IOException { PrintStream progressStream = System.out; int totalTrees = 10000; int totalTreesUsed = 0; progressStream.println("Reading trees (bar assumes 10,000 trees)..."); progressStream.println("0 25 50 75 100"); progressStream.println("|--------------|--------------|--------------|--------------|"); int stepSize = totalTrees / 60; if (stepSize < 1) stepSize = 1; TreeLineages treeLineages = new TreeLineages(); FileReader fileReader = new FileReader(inputFile.getFile()); jebl.evolution.io.NexusImporter importer = new NexusImporter(fileReader); try { totalTrees = 0; while (importer.hasTree()) { RootedTree tree = (RootedTree)importer.importNextTree(); if (totalTrees >= inputFile.getBurnin()) { treeLineages.addTree(tree); totalTreesUsed += 1; } if (totalTrees > 0 && totalTrees % stepSize == 0) { progressStream.print("*"); progressStream.flush(); } totalTrees++; } } catch (ImportException e) { System.err.println("Error Parsing Input Tree: " + e.getMessage()); return null; } fileReader.close(); progressStream.println(); progressStream.println(); if (totalTrees < 1) { System.err.println("No trees"); return null; } if (totalTreesUsed <= 1) { if (inputFile.getBurnin() > 0) { System.err.println("No trees to use: burnin too high"); return null; } } progressStream.println("Total trees read: " + totalTrees); if (inputFile.getBurnin() > 0) { progressStream.println("Ignoring first " + inputFile.getBurnin() + " trees."); } treeLineages.setupTrees(); return treeLineages; } private int processTrees1(InputFile inputFile) throws IOException { PrintStream progressStream = System.out; int totalTrees = 10000; int totalTreesUsed = 0; progressStream.println("Reading trees (bar assumes 10,000 trees)..."); progressStream.println("0 25 50 75 100"); progressStream.println("|--------------|--------------|--------------|--------------|"); int stepSize = totalTrees / 60; if (stepSize < 1) stepSize = 1; CladeSystem cladeSystem = document.getCladeSystem(); FileReader fileReader = new FileReader(inputFile.getFile()); TreeImporter importer = new dr.evolution.io.NexusImporter(fileReader); try { totalTrees = 0; while (importer.hasTree()) { Tree tree = importer.importNextTree(); if (totalTrees >= inputFile.getBurnin()) { cladeSystem.add(tree, true); totalTreesUsed += 1; } if (totalTrees > 0 && totalTrees % stepSize == 0) { progressStream.print("*"); progressStream.flush(); } totalTrees++; } } catch (Importer.ImportException e) { System.err.println("Error Parsing Input Tree: " + e.getMessage()); return 0; } fileReader.close(); progressStream.println(); progressStream.println(); if (totalTrees < 1) { System.err.println("No trees"); return 0; } if (totalTreesUsed <= 1) { if (inputFile.getBurnin() > 0) { System.err.println("No trees to use: burnin too high"); return 0; } } cladeSystem.normalizeClades(totalTreesUsed); progressStream.println("Total trees read: " + totalTrees); if (inputFile.getBurnin() > 0) { progressStream.println("Ignoring first " + inputFile.getBurnin() + " trees."); } int cladeCount = cladeSystem.getCladeMap().keySet().size(); progressStream.println("Total unique clades: " + cladeCount); progressStream.println(); progressStream.println("Processing trees for correlated clades:"); fileReader = new FileReader(inputFile.getFile()); importer = new dr.evolution.io.NexusImporter(fileReader); try { totalTrees = 0; while (importer.hasTree()) { Tree tree = importer.importNextTree(); if (totalTrees >= inputFile.getBurnin()) { cladeSystem.addCooccurances(tree); } if (totalTrees > 0 && totalTrees % stepSize == 0) { progressStream.print("*"); progressStream.flush(); } totalTrees++; } } catch (Importer.ImportException e) { System.err.println("Error Parsing Input Tree: " + e.getMessage()); return 0; } fileReader.close(); progressStream.println(); progressStream.println(); double THRESHOLD = 0.05; PrintWriter writer = new PrintWriter("clade_co-occurance.txt"); writer.println("source\tsize\ttarget\tco-occurence"); java.util.List<CladeSystem.Clade> allClades = cladeSystem.getClades(); for (CladeSystem.Clade clade1 : allClades) { String name1; int card1 = clade1.bits.cardinality(); if (card1 == 1) { name1 = clade1.label; } else { name1 = "clade" + (clade1.index + 1); } if (clade1.parents != null) { for (CladeSystem.Clade clade2 : clade1.parents.keySet()) { String name2; int card2 = clade2.bits.cardinality(); name2 = "clade" + (clade2.index+1); double value = clade1.parents.get(clade2); value /= totalTreesUsed; if (value > THRESHOLD) { if (card1 > card2) { writer.println(name1 + "_" + card1 + "\t" + card1 + "\t" + name2 + "_" + card2 + "\t" + value); } else { writer.println(name2 + "_" + card2 + "\t" + card2 + "\t" + name1 + "_" + card1 + "\t" + value); } } } } } writer.close(); writer = new PrintWriter("clade_frequency.txt"); writer.println("source\tsize\tfrequency"); for (CladeSystem.Clade clade1 : allClades) { String name1; int card1 = clade1.bits.cardinality(); if (card1 == 1) { name1 = clade1.label; } else { name1 = "clade" + (clade1.index + 1); } double value = clade1.count; value /= totalTreesUsed; if (value > THRESHOLD) { writer.println(name1 + "_" + card1 + "\t" + card1 + "\t" + value); } } writer.close(); progressStream.println(); cladePlotter.setCladeSystem(cladeSystem); return totalTreesUsed; } public void setStatusMessage(String text) { statusLabel.setText(text); } public final boolean doGenerate() { try { // generator.checkOptions(); } catch (IllegalArgumentException iae) { JOptionPane.showMessageDialog(this, iae.getMessage(), "Unable to generate file", JOptionPane.ERROR_MESSAGE); return false; } // offer stem as default // exportChooser.setSelectedFile(new File(beautiOptions.fileNameStem + ".xml")); final int returnVal = exportChooser.showSaveDialog(this); if( returnVal == JFileChooser.APPROVE_OPTION ) { File file = exportChooser.getSelectedFile(); int n = JOptionPane.YES_OPTION; if (file.exists()) { n = JOptionPane.showConfirmDialog(this, file.getName(), "Overwrite the exsting file?", JOptionPane.YES_NO_OPTION); } if (n == JOptionPane.YES_OPTION) { try { generate(file); } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to generate file: " + ioe.getMessage(), "Unable to generate file", JOptionPane.ERROR_MESSAGE); return false; } } else { doGenerate(); } } clearDirty(); return true; } protected void generate(File file) throws IOException { // getAllOptions(); FileWriter fw = new FileWriter(file); // generator.generateXML(fw); fw.close(); } public JComponent getExportableComponent() { JComponent exportable = null; Component comp = tabbedPane.getSelectedComponent(); if (comp instanceof Exportable) { exportable = ((Exportable) comp).getExportableComponent(); } else if (comp instanceof JComponent) { exportable = (JComponent) comp; } return exportable; } public boolean doSave() { return doSaveAs(); } public boolean doSaveAs() { FileDialog dialog = new FileDialog(this, "Save Template As...", FileDialog.SAVE); dialog.setVisible(true); if (dialog.getFile() == null) { // the dialog was cancelled... return false; } File file = new File(dialog.getDirectory(), dialog.getFile()); try { if (writeToFile(file)) { clearDirty(); } } catch (IOException ioe) { JOptionPane.showMessageDialog(this, "Unable to save file: " + ioe, "Unable to save file", JOptionPane.ERROR_MESSAGE); } return true; } public Action getImportAction() { return importAlignmentAction; } protected AbstractAction importAlignmentAction = new AbstractAction("Import Alignment...") { private static final long serialVersionUID = 3217702096314745005L; public void actionPerformed(java.awt.event.ActionEvent ae) { doImport(); } }; // public Action getImportTraitsAction() { // return importTraitsAction; // } // // protected AbstractAction importTraitsAction = new AbstractAction("Import Traits...") { // private static final long serialVersionUID = 3217702096314745005L; // // public void actionPerformed(java.awt.event.ActionEvent ae) { // doImportTraits(); // } // }; public Action getExportAction() { return generateAction; } protected AbstractAction generateAction = new AbstractAction("Generate Map File...", gearIcon) { private static final long serialVersionUID = -5329102618630268783L; public void actionPerformed(java.awt.event.ActionEvent ae) { doGenerate(); } }; }