/*
* MainFrame.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.bss;
import jam.framework.DocumentFrame;
import jam.framework.Exportable;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.plaf.BorderUIResource;
import dr.app.beagle.tools.BeagleSequenceSimulator;
import dr.app.beagle.tools.Partition;
import dr.evolution.alignment.SimpleAlignment;
import dr.evomodel.tree.TreeModel;
import dr.math.MathUtils;
/**
* @author Filip Bielejec
* @version $Id$
*/
@SuppressWarnings("serial")
public class MainFrame extends DocumentFrame implements FileMenuHandler {
private PartitionDataList dataList;
private BeagleSequenceSimulator beagleSequenceSimulator;
private final String TAXA_TAB_NAME = "Taxa";
private final String TREES_TAB_NAME = "Data";
private final String PARTITIONS_TAB_NAME = "Partitions";
private final String SIMULATION_TAB_NAME = "Simulation";
private final String TERMINAL_TAB_NAME = "Terminal";
private JTabbedPane tabbedPane = new JTabbedPane();
private TaxaPanel taxaPanel;
private TreesPanel treesPanel;
private PartitionsPanel partitionsPanel;
private SimulationPanel simulationPanel;
private TerminalPanel terminalPanel;
private JLabel statusLabel;
private JProgressBar progressBar;
private File workingDirectory = null;
public MainFrame(String title) {
super();
setTitle(title);
dataList = new PartitionDataList();
dataList.add(new PartitionData());
}// END: Constructor
@Override
protected void initializeComponents() {
setSize(new Dimension(1300, 600));
setMinimumSize(new Dimension(260, 100));
taxaPanel = new TaxaPanel(dataList);
treesPanel = new TreesPanel(this, dataList);
partitionsPanel = new PartitionsPanel(dataList);
simulationPanel = new SimulationPanel(this, dataList);
terminalPanel = new TerminalPanel();
tabbedPane.addTab(TAXA_TAB_NAME, null, taxaPanel);
tabbedPane.addTab(TREES_TAB_NAME, null, treesPanel);
tabbedPane.addTab(PARTITIONS_TAB_NAME, null, partitionsPanel);
tabbedPane.addTab(SIMULATION_TAB_NAME, null, simulationPanel);
tabbedPane.addTab(TERMINAL_TAB_NAME, null, terminalPanel);
statusLabel = new JLabel("No taxa loaded");
JPanel progressPanel = new JPanel(new BorderLayout(0, 0));
progressBar = new JProgressBar();
progressPanel.add(progressBar, BorderLayout.CENTER);
JPanel statusPanel = new JPanel(new BorderLayout(0, 0));
statusPanel.add(statusLabel, BorderLayout.CENTER);
statusPanel.add(progressPanel, BorderLayout.EAST);
statusPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(
new Insets(0, 6, 0, 6)));
JPanel tabbedPanePanel = new JPanel(new BorderLayout(0, 0));
tabbedPanePanel.add(tabbedPane, BorderLayout.CENTER);
tabbedPanePanel.add(statusPanel, BorderLayout.SOUTH);
tabbedPanePanel.setBorder(new BorderUIResource.EmptyBorderUIResource(
new Insets(12, 12, 12, 12)));
getContentPane().setLayout(new java.awt.BorderLayout(0, 0));
getContentPane().add(tabbedPanePanel, BorderLayout.CENTER);
tabbedPane.setSelectedComponent(treesPanel);
}// END: initializeComponents
// ////////////////
// ---SIMULATE---//
// ////////////////
// file chooser
public void doExport() {
if (getTreesCount() == 0) {
tabbedPane.setSelectedComponent(treesPanel);
Utils.showDialog("Please load at least one tree file before generating alignment.");
} else {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Simulate...");
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(workingDirectory);
int returnVal = chooser.showSaveDialog(Utils.getActiveFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
collectAllSettings();
generateNumberOfSimulations(file);
File tmpDir = chooser.getCurrentDirectory();
if (tmpDir != null) {
workingDirectory = tmpDir;
}
}// END: approve check
}// END: tree loaded check
}// END: doExport
// threading, UI, exceptions handling
private void generateNumberOfSimulations(final File outFile) {
setBusy();
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
ArrayList<TreeModel> simulatedTreeModelList = new ArrayList<TreeModel>();
// Executed in background thread
public Void doInBackground() {
try {
if (BeagleSequenceSimulatorApp.VERBOSE) {
Utils.printPartitionDataList(dataList);
System.out.println();
}
long startingSeed = dataList.startingSeed;
for (int i = 0; i < dataList.simulationsCount; i++) {
String fullPath = Utils.getMultipleWritePath(outFile,
dataList.outputFormat.toString().toLowerCase(), i);
PrintWriter writer = new PrintWriter(new FileWriter(
fullPath));
ArrayList<Partition> partitionsList = new ArrayList<Partition>();
for (PartitionData data : dataList) {
if (data.record == null) {
writer.close();
throw new RuntimeException(
"Set data in Partitions tab for "
+ (partitionsList.size() + 1)
+ " partition.");
} else {
TreeModel treeModel = data.createTreeModel();
simulatedTreeModelList.add(treeModel);
// create partition
Partition partition = new Partition(
treeModel, //
data.createBranchModel(), //
data.createSiteRateModel(), //
data.createClockRateModel(), //
data.createFrequencyModel(), //
data.from - 1, // from
data.to - 1, // to
data.every // every
);
if (data.ancestralSequenceString != null) {
partition.setRootSequence(data.createAncestralSequence());
}
partitionsList.add(partition);
}
}// END: data list loop
if (dataList.setSeed) {
MathUtils.setSeed(startingSeed);
startingSeed += 1;
}
beagleSequenceSimulator = new BeagleSequenceSimulator(
partitionsList
);
SimpleAlignment alignment = beagleSequenceSimulator.simulate(dataList.useParallel, dataList.outputAncestralSequences);
alignment.setOutputType(dataList.outputFormat);
// if (dataList.outputFormat == SimpleAlignment.OutputType.NEXUS) {
// alignment.setOutputType(dataList.outputFormat);
// } else if(dataList.outputFormat == SimpleAlignment.OutputType.XML) {
// alignment.setOutputType(dataList.outputFormat);
// }else {
// //
// }
writer.println(alignment.toString());
writer.close();
}// END: simulationsCount loop
} catch (Exception e) {
Utils.handleException(e);
setStatus("Exception occured.");
setIdle();
}
return null;
}// END: doInBackground
// Executed in event dispatch thread
public void done() {
// LinkedHashMap<Integer, LinkedHashMap<NodeRef, int[]>> partitionSequencesMap = beagleSequenceSimulator.getPartitionSequencesMap();
terminalPanel.setText(Utils.partitionDataListToString(dataList, simulatedTreeModelList
// , beagleSequenceSimulator.getPartitionSequencesMap()
));
setStatus("Generated " + Utils.getSiteCount(dataList) + " sites.");
setIdle();
}// END: done
};
worker.execute();
}// END: generateNumberOfSimulations
// ////////////////////
// ---GENERATE XML---//
// ////////////////////
public final void doGenerateXML() {
if (getTreesCount() == 0) {
tabbedPane.setSelectedComponent(treesPanel);
Utils.showDialog("Please load at least one tree file before generating XML.");
} else {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Generate XML...");
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(workingDirectory);
int returnVal = chooser.showSaveDialog(Utils.getActiveFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
generateXML(file);
File tmpDir = chooser.getCurrentDirectory();
if (tmpDir != null) {
workingDirectory = tmpDir;
}
}// END: approve check
}// END: tree loaded check
}// END: doGenerateXML
private void generateXML(final File outFile) {
setBusy();
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
public Void doInBackground() {
try {
collectAllSettings();
XMLGenerator xmlGenerator = new XMLGenerator(dataList);
xmlGenerator.generateXML(outFile);
} catch (Exception e) {
Utils.handleException(e);
setStatus("Exception occured.");
setIdle();
}
return null;
}// END: doInBackground
// Executed in event dispatch thread
public void done() {
setStatus("Generated " + outFile);
setIdle();
}// END: done
};
worker.execute();
}// END: generateNumberOfSimulations
// /////////////////
// ---MAIN MENU---//
// /////////////////
// @Override
public Action getSaveSettingsAction() {
return new AbstractAction("Save settings...") {
public void actionPerformed(ActionEvent ae) {
doSaveSettings();
}
};
}// END: generateXMLAction
private void doSaveSettings() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Save as...");
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(workingDirectory);
int returnVal = chooser.showSaveDialog(Utils.getActiveFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
saveSettings(file);
File tmpDir = chooser.getCurrentDirectory();
if (tmpDir != null) {
workingDirectory = tmpDir;
}
setStatus("Saved as " + file.getAbsolutePath());
}// END: approve check
}// END: saveSettings
private void saveSettings(File file) {
try {
String fullPath = Utils.getWritePath(file, "bss");
OutputStream fileOut = new FileOutputStream(new File(fullPath));
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(dataList);
out.close();
fileOut.close();
} catch (FileNotFoundException e) {
Utils.handleException(e);
} catch (IOException e) {
Utils.handleException(e);
}
}// END: saveSettings
// @Override
public Action getLoadSettingsAction() {
return new AbstractAction("Load settings...") {
public void actionPerformed(ActionEvent ae) {
doLoadSettings();
}
};
}// END: generateXMLAction
private void doLoadSettings() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Load...");
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(workingDirectory);
int returnVal = chooser.showOpenDialog(Utils.getActiveFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
loadSettings(file);
File tmpDir = chooser.getCurrentDirectory();
if (tmpDir != null) {
workingDirectory = tmpDir;
}
setStatus("Loaded " + file.getAbsolutePath());
}// END: approve check
}// END: doLoadSettings
private void loadSettings(File file) {
try {
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fileIn);
dataList = (PartitionDataList) in.readObject();
in.close();
fileIn.close();
if (BeagleSequenceSimulatorApp.VERBOSE) {
Utils.printPartitionDataList(dataList);
System.out.println();
}
partitionsPanel.updatePartitionTable(dataList);
taxaPanel.updateTaxaTable(dataList);
treesPanel.updateTreesTable(dataList);
simulationPanel.updateSimulationPanel(dataList);
} catch (IOException ioe) {
Utils.handleException(
ioe,
"Unable to read BSS file. "
+ "BSS can only read files created by 'Saving' within BSS. "
+ "It cannot read XML files.");
} catch (ClassNotFoundException cnfe) {
Utils.handleException(cnfe);
}// END: try-catch block
}// END: loadSettings
@Override
protected boolean readFromFile(File arg0) throws IOException {
return false;
}
@Override
protected boolean writeToFile(File arg0) throws IOException {
return false;
}
// //////////////////////
// ---SHARED METHODS---//
// //////////////////////
public File getWorkingDirectory() {
return workingDirectory;
}// END: getWorkingDirectory
public void setWorkingDirectory(File workingDirectory) {
this.workingDirectory = workingDirectory;
}// END: setWorkingDirectory
public void collectAllSettings() {
// frequencyPanel.collectSettings();
// substModelPanel.collectSettings();
// clockPanel.collectSettings();
// sitePanel.collectSettings();
simulationPanel.collectSettings();
}// END: collectAllSettings
public void fireTaxaChanged() {
if (SwingUtilities.isEventDispatchThread()) {
doTaxaChanged();
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doTaxaChanged();
}
});
}// END: edt check
}// END: fireTaxaChanged
private void doTaxaChanged() {
treesPanel.fireTableDataChanged();
taxaPanel.fireTaxaChanged();
setStatus(Integer.toString(dataList.allTaxa
.getTaxonCount()) + " taxa loaded.");
}//END: doTaxaChanged
public void setBusy() {
if (SwingUtilities.isEventDispatchThread()) {
simulationPanel.setBusy();
progressBar.setIndeterminate(true);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
simulationPanel.setBusy();
progressBar.setIndeterminate(true);
}
});
}// END: edt check
}// END: setBusy
public void setIdle() {
if (SwingUtilities.isEventDispatchThread()) {
simulationPanel.setIdle();
progressBar.setIndeterminate(false);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
simulationPanel.setIdle();
progressBar.setIndeterminate(false);
}
});
}// END: edt check
}// END: setIdle
public void setStatus(final String status) {
if (SwingUtilities.isEventDispatchThread()) {
statusLabel.setText(status);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
statusLabel.setText(status);
}
});
}// END: edt check
}// END: setStatus
public void hideTreeColumn() {
if (SwingUtilities.isEventDispatchThread()) {
partitionsPanel.hideTreeColumn();
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
partitionsPanel.hideTreeColumn();
}
});
}// END: edt check
}// END: hideTreeColumn
public void showTreeColumn() {
if (SwingUtilities.isEventDispatchThread()) {
partitionsPanel.showTreeColumn();
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
partitionsPanel.showTreeColumn();
}
});
}// END: edt check
}// END: showTreeColumn
public void disableTaxaPanel() {
final int index = Utils.getTabbedPaneComponentIndex(tabbedPane, TAXA_TAB_NAME);
if (SwingUtilities.isEventDispatchThread()) {
tabbedPane.setEnabledAt(index, false);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
tabbedPane.setEnabledAt(index, false);
}
});
}// END: edt check
}// END: disableTaxaPanel
public void enableTaxaPanel() {
final int index = Utils.getTabbedPaneComponentIndex(tabbedPane, TAXA_TAB_NAME);
if (SwingUtilities.isEventDispatchThread()) {
tabbedPane.setEnabledAt(index, true);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
tabbedPane.setEnabledAt(index, true);
}
});
}// END: edt check
}// END: enableTaxaPanel
private int getTreesCount() {
return dataList.recordsList.size();
}// END: getTreesCount
// @Override // Please use Java 1.5
public JComponent getExportableComponent() {
JComponent exportable = null;
Component component = tabbedPane.getSelectedComponent();
if (component instanceof Exportable) {
exportable = ((Exportable) component).getExportableComponent();
} else if (component instanceof JComponent) {
exportable = (JComponent) component;
}
return exportable;
}// END: getExportableComponent
}// END: class