// This file is part of BULL, a program for phylogenetic simulations // most of the code was written by Mark T. Holder. // It 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. // // Some of the code is from publically available source by Paul Lewis, Ziheng Yang, // John Huelsenbeck, David Swofford , and others (as noted in the code). // In fact the main structure of the program was created by modifying Paul Lewis' // basiccmdline.cpp from his NCL // // This code was used in Mark's dissertation, some changes were made in order to // get it to compile on gcc. It is possible that this porting introduced bugs (very // little debugging has been done on UNIX platforms). I would suggest checking // the simulator by generating data on trees with short branches, etc. // This file is part of BULL, a program for phylogenetic simulations // most of the code was written by Mark T. Holder. // It 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. // // Some of the code is from publically available source by Paul Lewis, Ziheng Yang, // John Huelsenbeck, David Swofford , and others (as noted in the code). // In fact the main structure of the program was created by modifying Paul Lewis' // basiccmdline.cpp from his NCL // // This code was used in Mark's dissertation, some changes were made in order to // get it to compile on gcc. It is possible that this porting introduced bugs (very // little debugging has been done on UNIX platforms). I would suggest checking // the simulator by generating data on trees with short branches, etc. #include "nexus_defs.hpp" #include "xbull.hpp" #include "nexus_token.hpp" #include "nexus.hpp" #define NCL_NAME_AND_VERSION "NCL version 2.0" #define NCL_COPYRIGHT "Copyright (c) 1999 by Paul O. Lewis" #define NCL_HOMEPAGEURL "http://alleyn.eeb.uconn.edu/ncl/" /** * @class Nexus * @file nexus.hpp * @file nexus.cpp * @author Paul O. Lewis * @copyright Copyright © 1999. All Rights Reserved. * @variable blockList [NexusBlock*:protected] pointer to first block in list of blocks * @see NexusBlock * @see NexusReader * @see NexusToken * @see XBull * * This is the class that orchestrates the reading of a Nexus data file. * An object of this class should be created, and objects of any block classes * that are expected to be needed should be added to blockList using the Add * member function. The Execute member function is then called, which reads the * data file until encountering a block name, at which point the correct block * is looked up in blockList and that object's Read method called. */ /** * @constructor * * Default constructor * Initializes the blockList data member to NULL. */ Nexus::Nexus() : blockList(NULL) { } /** * @destructor * * Does nothing. */ Nexus::~Nexus() { } /** * @method Add [void:public] * @param newBlock [NexusBlock*] a pointer to an existing block object * * Adds newBlock to the end of the list of NexusBlock objects growing * from blockList. If blockList points to NULL, this function sets * blockList to point to newBlock. Calls SetNexus method of newBlock * to inform newBlock of the Nexus object that now owns it. This * is useful when the newBlock object needs to communicate with the * outside world through the Nexus object, such as when it issues * progress reports as it is reading the contents of its block. */ void Nexus::Add( NexusBlock* newBlock ) { assert( newBlock != NULL ); newBlock->SetNexus(this); if ( !blockList ) blockList = newBlock; else { // add new block to end of list NexusBlock* curr; for ( curr = blockList; curr && curr->next; ) curr = curr->next; assert( curr && !curr->next ); curr->next = newBlock; } } /** * @method BlockListEmpty [bool:public] * * If blockList data member still equals NULL, returns true; * otherwise, returns false. The blockList will not be equal * to NULL if the Add function has been called to add a block * object to the list. */ bool Nexus::BlockListEmpty() { return ( blockList == NULL ? true : false ); } /** * @method DebugReportBlock [virtual void:protected] * @param nexusBlock [NexusBlock&] the block that should be reported * * This function was created for purposes of debugging a new NexusBlock. * This version does nothing; to create an active DebugReportBlock function, * override this version in the derived class and call the Report function * of nexusBlock. This function is called whenever the main Nexus Execute function * encounters the [&spillall] command comment between blocks in the data file. * The Execute function goes through all blocks and passes them, in turn, to this * DebugReportBlock function so that their contents are displayed. Placing the * [&spillall] command comment between different versions of a block allows * multiple blocks of the same type to be tested using one long data file. * Say you are interested in testing whether the normal, transpose, and * interleave format of a matrix can all be read correctly. If you put three * versions of the block in the data file one after the other, the second one * will wipe out the first, and the third one will wipe out the second, unless * you have a way to report on each one before the next one is read. This * function provides that ability. */ void Nexus::DebugReportBlock( NexusBlock& /* nexusBlock */ ) { // Derive me and uncomment the following line in the derived version // nexusBlock.Report(out); // Note that your derived Nexus object must have its own ostream object (out) } /** * @method Detach [void:public] * @param oldBlock [NexusBlock*] a pointer to an existing block object * * Detaches oldBlock from the list of NexusBlock objects growing * from blockList. If blockList itself points to oldBlock, this * function sets blockList to point to oldBlock->next. * Note: oldBlock is not deleted, it is simple detached from the * linked list. No harm is done in Detaching a block pointer * that has already been detached previously; if oldBlock is * not found in the block list, Detach simply returns quietly. * If oldBlock is found, its SetNexus object is called to set * the Nexus pointer to NULL, indicating that it is no longer * owned by (i.e., attached to) a Nexus object. */ void Nexus::Detach( NexusBlock* oldBlock ) { assert( oldBlock != NULL ); // Should call BlockListEmpty function first to make sure // there are blocks to detach // assert( blockList != NULL ); if ( blockList == oldBlock ) { blockList = oldBlock->next; oldBlock->SetNexus(NULL); } else { // try to find oldBlock in list and detach if found NexusBlock* curr = blockList; NexusBlock* currNext = blockList->next; for ( ; currNext != NULL && currNext != oldBlock; ) currNext = currNext->next; if ( currNext == oldBlock ) { curr->next = currNext->next; currNext->next = NULL; oldBlock->SetNexus(NULL); } } } /** * @method EnteringBlock [virtual void:public] * @param blockName [char*] the name of the block being entered * * This function is called when a block named blockName has just * been entered and is about to be read. Override this pure virtual * function to provide an indication of progress as the Nexus file * is being read. */ // virtual void EnteringBlock( char* blockName ) = 0; /** * @method Execute [void:public] * @param token [NexusToken&] the token object used to grab Nexus tokens * @param in [istream&] the input stream from which to read * * Reads the Nexus data file from the input stream in. This function * is responsible for reading through the name of a each block. Once * it has read a block name, it searches blockList for a block object * to handle reading the remainder of the block's contents. The block * object is responsible for reading the end or endblock command as well * as the trailing semicolon. This function also handles reading * comments that are outside of blocks, as well as the initial #NEXUS * keyword. */ void Nexus::Execute( NexusToken& token ) { std::string errormsg; try { token.GetNextToken(); } catch( XBull x ) { NexusError( token.errormsg, 0, 0, 0 ); return; } if ( !token.Equals("#NEXUS") ) { errormsg = "Expecting #NEXUS to be the first token in the file, but found "; errormsg += token.GetToken(); errormsg += " instead"; NexusError( errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn() ); return; } ExecuteStarting(); for (;;) { token.SetLabileFlagBit( NexusToken::saveCommandComments ); token.GetNextToken(); if ( token.AtEOF() ) break; if ( token.Equals("BEGIN") ) { token.GetNextToken(); NexusBlock* curr; for ( curr = blockList; curr != NULL; curr = curr->next ) { if ( !token.Equals( curr->GetID() ) ) continue; EnteringBlock( token.GetToken() ); curr->Reset(); try { curr->Read( token ); } catch( XBull x ) { if (curr->errormsg.length() == 0) NexusError( x.msg, x.pos, x.line, x.col ); else NexusError( curr->errormsg, x.pos, x.line, x.col ); curr->Reset(); return; } break; } if ( curr == NULL ) { std::string currBlock = token.GetToken(); SkippingBlock( currBlock ); for (;;) { token.GetNextToken(); if ( token.Equals("END") || token.Equals("ENDBLOCK") ) { token.GetNextToken(); if ( !token.Equals(";") ) { errormsg = "Expecting ';' after END or ENDBLOCK command, but found "; errormsg += token.GetToken(); errormsg += " instead"; NexusError( errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn() ); return; } break; } if ( token.AtEOF() ) { errormsg = "Encountered end of file before END or ENDBLOCK in block "; errormsg += currBlock; NexusError( errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn() ); return; } } } // if token not found amongst known block IDs } // if token equals BEGIN else if ( token.Equals("&SHOWALL") ) { NexusBlock* curr; for ( curr = blockList; curr != NULL; curr = curr->next ) { DebugReportBlock(*curr); } } else if ( token.Equals("&LEAVE") ) { break; } } // for (;;) ExecuteStopping(); } /** * @method NCLCopyrightNotice [char*:public] * * Returns a string containing the copyright notice * for the Nexus Class Library, useful for * reporting the use of this library by programs * that interact with the user. */ char* Nexus::NCLCopyrightNotice() { return NCL_COPYRIGHT; } /** * @method NCLHomePageURL [char*:public] * * Returns a string containing the URL for the * Nexus Class Library Home Page on the World * Wide Web. */ char* Nexus::NCLHomePageURL() { return NCL_HOMEPAGEURL; } /** * @method NCLNameAndVersion [char*:public] * * Returns a string containing the name and current * version of the Nexus Class Library, useful for * reporting the use of this library by programs * that interact with the user. */ char* Nexus::NCLNameAndVersion() { return NCL_NAME_AND_VERSION; }