/*
* NexusApplicationImporter.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.beauti.util;
import dr.evomodel.substmodel.nucleotide.NucModelType;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.PartitionSubstitutionModel;
import dr.app.beauti.options.PartitionTreeModel;
import dr.app.beauti.types.StartingTreeType;
import dr.evolution.alignment.Alignment;
import dr.evolution.alignment.CharSetAlignment;
import dr.evolution.io.NexusImporter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* Class for importing PAUP, MrBayes and Rhino NEXUS file format
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: NexusApplicationImporter.java,v 1.4 2005/07/11 14:07:25 rambaut Exp $
*/
public class NexusApplicationImporter extends NexusImporter {
public static final NexusBlock ASSUMPTIONS_BLOCK = new NexusBlock("ASSUMPTIONS");
public static final NexusBlock SETS_BLOCK = new NexusBlock("SETS");
public static final NexusBlock PAUP_BLOCK = new NexusBlock("PAUP");
public static final NexusBlock MRBAYES_BLOCK = new NexusBlock("MRBAYES");
/**
* @param reader a reader to read the Nexus format from
*/
public NexusApplicationImporter(Reader reader) {
super(reader);
setCommentDelimiters('[', ']', '\0');
}
public NexusApplicationImporter(Reader reader, Writer commentWriter) {
super(reader, commentWriter);
setCommentDelimiters('[', ']', '\0');
}
/**
* This function returns an enum class to specify what the
* block given by blockName is.
*/
public NexusBlock findBlockName(String blockName) {
if (blockName.equalsIgnoreCase(ASSUMPTIONS_BLOCK.toString())) {
return ASSUMPTIONS_BLOCK;
} else if (blockName.equalsIgnoreCase(SETS_BLOCK.toString())) {
return SETS_BLOCK;
} else if (blockName.equalsIgnoreCase(PAUP_BLOCK.toString())) {
return PAUP_BLOCK;
} else if (blockName.equalsIgnoreCase(MRBAYES_BLOCK.toString())) {
return MRBAYES_BLOCK;
} else {
return super.findBlockName(blockName);
}
}
/**
* Parses an 'Assumptions' block.
*
* @param charSets a list of char sets to *add* to if any are defined in PAUP block
* @throws dr.evolution.io.Importer.ImportException
* if Assumptions block is poorly formed
* @throws java.io.IOException if I/O fails
*/
public void parseAssumptionsBlock(List<CharSet> charSets) throws ImportException, IOException {
boolean done = false;
while (!done) {
String command = readToken(";");
if (command.equalsIgnoreCase("ENDBLOCK") || command.equalsIgnoreCase("END")) {
done = true;
} else if (match("CHARSET", command, 5)) {
if (getLastDelimiter() != ';') {
charSets.add(readCharSetCommand());
}
} else {
System.err.println("The command, '" + command + "', is not used by BEAST and has been ignored");
}
}
}
/**
* Parses a 'PAUP' block.
*
* @param options the BEAUti options
* @param charSets a list of char sets to *add* to if any are defined in PAUP block
* @return a partition model representing the model defined in the PAUP block
* @throws dr.evolution.io.Importer.ImportException
* if PAUP block is poorly formed
* @throws java.io.IOException if I/O fails
*/
public PartitionSubstitutionModel parsePAUPBlock(BeautiOptions options, List<CharSet> charSets) throws ImportException, IOException {
PartitionSubstitutionModel model = new PartitionSubstitutionModel(options, "nucs");
readTopLevelBlock(options, model, charSets);
return model;
}
/**
* Parses a 'MRBAYES' block.
*
* @param options the BEAUti options
* @param charSets a list of char sets to *add* to if any are defined in PAUP block
* @return a partition model representing the model defined in the MRBAYES block
* @throws dr.evolution.io.Importer.ImportException
* if MRBAYES block is poorly formed
* @throws java.io.IOException if I/O fails
*/
public PartitionSubstitutionModel parseMrBayesBlock(BeautiOptions options, List<CharSet> charSets) throws ImportException, IOException {
PartitionSubstitutionModel model = new PartitionSubstitutionModel(options, "nucs");
readTopLevelBlock(options, model, charSets);
return model;
}
private CharSet readCharSetCommand() throws ImportException, IOException {
String name = readToken("=;");
CharSet charSet = new CharSet(name);
// System.out.print("Char set " + name);
int from;
int to;
int every = 1;
while (getLastDelimiter() != ';') {
String token = readToken(";,");
// This was to remove spaces within each partition definition. But that means
// we can't read multiple definitions separated by spaces.
// while (getLastDelimiter() != ';' && getLastDelimiter() != ',') {
// token += readToken(";,");
// }
String[] parts = token.split("-");
// System.out.print(token + " ");
try {
if (parts.length == 2) {
from = Integer.parseInt(parts[0].trim());
String[] toParts = parts[1].split("\\\\");
if (toParts[0].trim().equals(".")) {
to = -1;
} else {
to = Integer.parseInt(toParts[0].trim());
}
every = 1;
if (toParts.length > 1) every = Integer.parseInt(toParts[1].trim());
} else if (parts.length == 1) {
from = Integer.parseInt(parts[0].trim());
to = from;
} else {
throw new ImportException("CharSet, " + name + ", unable to be parsed");
}
} catch (NumberFormatException nfe) {
throw new ImportException("CharSet, " + name + ", unable to be parsed");
}
charSet.addCharSetBlock(new CharSetBlock(from, to, every));
}
// System.out.println();
return charSet;
}
/**
* This method reads a PAUP or MrBayes block
*
* @param options the beauti options
* @param model the partition model
* @param charSets a list of char sets to *add* to if any are defined in PAUP block
* @throws dr.evolution.io.Importer.ImportException
* if top-level block is poorly formed
* @throws java.io.IOException if I/O fails
*/
private void readTopLevelBlock(BeautiOptions options, PartitionSubstitutionModel model, List<CharSet> charSets)
throws ImportException, IOException {
boolean done = false;
while (!done) {
String command = readToken(";");
if (command.equalsIgnoreCase("ENDBLOCK") || command.equalsIgnoreCase("END")) {
done = true;
} else if (match("HSEARCH", command, 2)) {
// Once we reach a search in PAUP then stop
done = true;
} else if (match("MCMC", command, 4)) {
if (getLastDelimiter() != ';') {
readMCMCCommand(options);
}
done = true;
} else if (match("MCMCP", command, 5)) {
if (getLastDelimiter() != ';') {
readMCMCCommand(options);
}
} else if (match("LSET", command, 2)) {
if (getLastDelimiter() != ';') {
readLSETCommand(model);
}
} else if (match("CHARSET", command, 5)) {
if (getLastDelimiter() != ';') {
charSets.add(readCharSetCommand());
}
} else {
System.err.println("The command, '" + command + "', is not used by BEAST and has been ignored");
}
}
}
private void readLSETCommand(PartitionSubstitutionModel model) throws ImportException, IOException {
boolean done = false;
while (!done) {
String subcommand = readToken("=;");
if (match("NST", subcommand, 2)) {
int nst = readInteger(";");
if (nst == 1) {
model.setNucSubstitutionModel(NucModelType.JC);
} else if (nst == 2) {
model.setNucSubstitutionModel(NucModelType.HKY);
} else if (nst == 6) {
model.setNucSubstitutionModel(NucModelType.GTR);
} else {
throw new BadFormatException("Bad value for NST subcommand of LSET command");
}
} else if (match("RATES", subcommand, 2)) {
String token = readToken(";");
if (match("EQUAL", token, 1)) {
model.setGammaHetero(false);
model.setInvarHetero(false);
} else if (match("GAMMA", token, 1)) {
model.setGammaHetero(true);
model.setInvarHetero(false);
} else if (match("PROPINV", token, 1)) {
model.setGammaHetero(false);
model.setInvarHetero(true);
} else if (match("INVGAMMA", token, 1)) {
model.setGammaHetero(true);
model.setInvarHetero(true);
} else if (match("ADGAMMA", token, 1)) {
System.err.println("The option, 'RATES=ADGAMMA', in the LSET command is not used by BEAST and has been ignored");
} else if (match("SITESPEC", token, 1)) {
System.err.println("The option, 'RATES=SITESPEC', in the LSET command is not used by BEAST and has been ignored");
} else {
throw new BadFormatException("Unknown value, '" + token + "'");
}
} else if (match("NGAMMACAT", subcommand, 2)) {
model.setGammaCategories(readInteger(";"));
} else {
System.err.println("The option, '" + subcommand + "', in the LSET command is not used by BEAST and has been ignored");
}
if (getLastDelimiter() == ';') {
done = true;
}
}
}
private void readMCMCCommand(BeautiOptions options) throws ImportException, IOException {
boolean done = false;
while (!done) {
String subcommand = readToken("=;");
if (match("NGEN", subcommand, 2)) {
options.chainLength = readInteger(";");
} else if (match("SAMPLEFREQ", subcommand, 2)) {
options.logEvery = readInteger(";");
} else if (match("PRINTFREQ", subcommand, 1)) {
options.echoEvery = readInteger(";");
} else if (match("FILENAME", subcommand, 1)) {
options.fileName = readToken(";");
} else if (match("BURNIN", subcommand, 1)) {
options.burnIn = readInteger(";");
} else if (match("STARTINGTREE", subcommand, 2)) {
String token = readToken(";");
if (match("USER", token, 1)) {
// How do we know what tree to use?
// options.startingTreeType = StartingTreeType.USER;
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
model.setStartingTreeType(StartingTreeType.USER);
}
} else if (match("RANDOM", token, 1)) {
// options.startingTreeType = StartingTreeType.RANDOM;
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
model.setStartingTreeType(StartingTreeType.RANDOM);
}
} else {
throw new BadFormatException("Unknown value, '" + token + "'");
}
} else {
System.err.println("The option, '" + subcommand + "', in the MCMC command is not used by BEAST and has been ignored");
}
if (getLastDelimiter() == ';') {
done = true;
}
}
}
private boolean match(String reference, String target, int min) throws ImportException {
if (target.length() < min) {
//throw new BadFormatException("Ambiguous command or subcommand, '" + target + "'");
}
return reference.startsWith(target.toUpperCase());
}
public class CharSet {
String name;
List<CharSetBlock> blocks;
public CharSet(String name) {
this.name = name;
blocks = new ArrayList<CharSetBlock>();
}
public List<CharSetBlock> getBlocks() {
return blocks;
}
public String getName() {
return name;
}
public void addCharSetBlock(CharSetBlock b) {
blocks.add(b);
}
public Alignment constructCharSetAlignment(Alignment alignment) {
return new CharSetAlignment(this, alignment);
}
}
public class CharSetBlock {
public CharSetBlock(int fromSite, int toSite, int every) {
this.fromSite = fromSite;
this.toSite = toSite;
this.every = every;
}
public int getFromSite() {
return fromSite;
}
public int getToSite() {
return toSite;
}
public int getEvery() {
return every;
}
private final int fromSite;
private final int toSite;
private final int every;
}
}