#include "phycas/phycas.h" #include "ncl/nxs_defs.hpp" #if NEW_NXS_BLOCK_AND_READER #include "ncl/nxs_reader.hpp" #include "ncl/nxs_block.hpp" #include "ncl/nxs_token.hpp" #include "ncl/nxs_exception.hpp" using std::vector; using std::string; void DisableBlockIfItCannotReadID(NxsBlock *nxsBlockReader, const VecString &activeBlockIDs); bool ncl::NCL::strictParsing = false; /*---------------------------------------------------------------------------------------------------------------------- | Disables any block that cannot read the activeBlockID id. | returns the enabled status before the command was executed (in case you need to restore) */ vector NxsReader::DisableAllBlocksExcept(const string &activeBlockID) { VecString v(1, activeBlockID); return DisableAllBlocksExcept(v); } /*---------------------------------------------------------------------------------------------------------------------- | Enables blocks with a true in the vector, Disables those with false. | When used with DisableAllBlocksExcept this assumes that the # and order of block readers hasn't changed */ void NxsReader::RestoreEnabledStatus(const vector &enableStatus) { NxsBlock *nxsBlockReader; vector::const_iterator esIt = enableStatus.begin(); for (ListOfBlockPtrs::iterator currIt = blockPtrs.begin(); esIt != enableStatus.end() && currIt != blockPtrs.end(); ++currIt, ++esIt) { nxsBlockReader = *currIt; if (*esIt) nxsBlockReader->Enable(); else nxsBlockReader->Disable(); } NxsBlockPtrShared temp = NxsBlockPtrShared(); //If we are get the pointer from a weak pointer, this shared pointer will keep the block around while it is read the block for(ListOfWeakBlockPtrs::iterator currIt = weakBlockPtrs.begin(); esIt != enableStatus.end() && currIt != weakBlockPtrs.end(); ++currIt, ++esIt) { temp = boost::make_shared(*currIt); nxsBlockReader = temp.get(); if (*esIt) nxsBlockReader->Enable(); else nxsBlockReader->Disable(); } } void DisableBlockIfItCannotReadID(NxsBlock *nxsBlockReader, const VecString &activeBlockIDs) { bool canRead = false; for (VecString::const_iterator abiIt = activeBlockIDs.begin(); abiIt != activeBlockIDs.end(); ++abiIt) { if (nxsBlockReader->CanReadBlockType(*abiIt)) { canRead = true; break; } } if (!canRead) nxsBlockReader->Disable(); } /*---------------------------------------------------------------------------------------------------------------------- | Disables any block that cannot read the any of the id's in activeBlockIDs. | returns the enabled status before the command was executed (in case you need to restore) */ vector NxsReader::DisableAllBlocksExcept(const VecString &activeBlockIDs) { NxsBlock *nxsBlockReader; vector retVec; for(ListOfBlockPtrs::iterator currIt = blockPtrs.begin(); currIt != blockPtrs.end(); ++currIt) { nxsBlockReader = *currIt; retVec.push_back(nxsBlockReader->IsEnabled()); DisableBlockIfItCannotReadID(nxsBlockReader, activeBlockIDs); } NxsBlockPtrShared temp = NxsBlockPtrShared(); //If we are get the pointer from a weak pointer, this shared pointer will keep the block around while it is read the block for(ListOfWeakBlockPtrs::iterator currIt = weakBlockPtrs.begin(); currIt != weakBlockPtrs.end(); ++currIt) { temp = boost::make_shared(*currIt); nxsBlockReader = temp.get(); retVec.push_back(nxsBlockReader->IsEnabled()); DisableBlockIfItCannotReadID(nxsBlockReader, activeBlockIDs); } return retVec; } /*---------------------------------------------------------------------------------------------------------------------- | Adds newBlock to the list of NxsBlock objects that can be used to read files and calls SetNexus method of newBlock | to inform newBlock of the NxsReader object that now calls it. */ void NxsReader::Add( NxsBlockWeakPtr newBlock) { assert(boost::make_shared(newBlock) != NxsBlockPtrShared()); boost::make_shared(newBlock)->SetNexus(this); weakBlockPtrs.push_back(newBlock); } /*---------------------------------------------------------------------------------------------------------------------- | Adds newBlock to the list of NxsBlock objects that can be used to read files and calls SetNexus method of newBlock | to inform newBlock of the NxsReader object that now calls it. */ void NxsReader::Add( NxsBlock *newBlock) { assert(newBlock != NULL); newBlock->SetNexus(this); blockPtrs.push_back(newBlock); } bool EqualsBare(NxsBlockWeakPtr w, NxsBlock *b); bool EqualsBare(NxsBlockWeakPtr w, NxsBlock *b) { return (b == boost::make_shared(w).get()); } /*---------------------------------------------------------------------------------------------------------------------- | Adds newBlock to the list of NxsBlock objects that can be used to read files and calls SetNexus method of newBlock | to inform newBlock of the NxsReader object that now calls it. */ void NxsReader::Detach( NxsBlockWeakPtr newBlock) { NxsBlockPtrShared p = boost::make_shared(newBlock); if (p != NxsBlockPtrShared()) Detach(p.get()); } /*---------------------------------------------------------------------------------------------------------------------- | Adds newBlock to the list of NxsBlock objects that can be used to read files and calls SetNexus method of newBlock | to inform newBlock of the NxsReader object that now calls it. */ void NxsReader::Detach( NxsBlock *newBlock) { assert(newBlock != NULL); newBlock->SetNexus(NULL); blockPtrs.remove(newBlock); // also remove the shared block pointer to the object // for (ListOfWeakBlockPtrs::iterator sbIt = weakBlockPtrs.begin(); sbIt != weakBlockPtrs.end(); ) { NxsBlockPtrShared p = boost::make_shared(*sbIt); if (p.get() == newBlock) sbIt = weakBlockPtrs.erase(sbIt); else ++sbIt; } } /*---------------------------------------------------------------------------------------------------------------------- | Calls the reset function for every NxsBlock object that NxsReader contains. */ void NxsReader::ResetAllBlocks() { for (ListOfBlockPtrs::iterator bIt = blockPtrs.begin(); bIt != blockPtrs.end(); ++bIt) (*bIt)->Reset(); for (ListOfWeakBlockPtrs::iterator sbIt = weakBlockPtrs.begin(); sbIt != weakBlockPtrs.end(); ++sbIt) boost::make_shared(*sbIt)->Reset(); } /*---------------------------------------------------------------------------------------------------------------------- | Reads the Nexus data file from the input stream provided by token. | This function is responsible for reading up to the name of a each block. Once it has read a block name, it searches | through the NxsBlock objects that have been Added for one that returns true for the function CanReadBlockType(). | (if no NxsBlocks are found. the NxsBlockSuppliers are asked if they can generate a NxsBlock to read the block) | If a NxsBlock reader is found NxsReader::UseNexusBlockToRead() is called with that block as an argument; | if not the block is skipped (and SkippingBlock() is called). | This function also implements reading the command comments LEAVE and SHOWALL between blocks. | The notifyStartStop argument is provided in case you do not wish the ExecuteStart and ExecuteStop functions to be | called. These functions are primarily used for creating and destroying a dialog box to show progress, and nested | Execute calls can thus cause problems (e.g., a dialog box is destroyed when the inner Execute calls ExecuteStop and | the outer Execute still expects the dialog box to be available). Specifying notifyStartStop false for all the | nested Execute calls thus allows the outermost Execute call to control creation and destruction of the dialog box. | returns true if the end of the file is reached without error. | | Errors in the parsing of NxsBlocks generate NxsExceptions. These are caught in this function, and NexusError() | is called to report the error, then false is returned. Thus, this function doesn't throw any NxsExceptions. */ bool NxsReader::Execute( NxsToken& token, bool notifyStartStop /* = true */) { // Check for valid stream with the first token being #NEXUS // string errormsg; bool cmdCommentFound = false; try { cmdCommentFound = token.ReadCommandCommentOrToken(); } catch(NxsException & x) { NexusError(x.msg, 0, 0, 0, x.cResultCode); return false; } if(notifyStartStop) ExecuteStarting(); if(token.Equals("#NEXUS")) cmdCommentFound = token.ReadCommandCommentOrToken(); else if (! ncl::NCL::allow(ncl::kNxsErrMissingPoundNexus)) { errormsg << "Expecting #NEXUS to be the first token in the file, but found " << token.GetTokenReference() << " instead"; NexusError(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn(), kCmdFailedGenerateMessage); return false; } bool leaveCommandEncountered = false; // read through the file until one of the following: EOF, LEAVE command comment, or an error in a block. // errors result in returning false. All NxsExceptions are caught, funnelled through NexusError(), and result in "return false;" // for(;;) { if(token.AtEOF()) break; if (cmdCommentFound) { unsigned commandCommentIndex = token.GetNextCommentIndex('&', 0); if (commandCommentIndex != UINT_MAX) { NxsComment cmdComment = token.GetComment(commandCommentIndex); if (cmdComment.GetLocationInToken() < 1) { if (StrEquals(cmdComment.GetCommentText(), "SHOWALL", ncl::kStringNoRespectCase)) { ListOfBlockPtrs::iterator currIt = blockPtrs.begin(); for(; currIt != blockPtrs.end(); ++currIt) DebugReportBlock(**currIt); ListOfWeakBlockPtrs::iterator scurrIt = weakBlockPtrs.begin(); NxsBlockPtrShared p = NxsBlockPtrShared(); for(; scurrIt != weakBlockPtrs.end(); ++scurrIt) { p = boost::make_shared(*scurrIt); DebugReportBlock(*p); } } else if (StrEquals(cmdComment.GetCommentText(), "LEAVE", ncl::kStringNoRespectCase)) { leaveCommandEncountered = true; break; } } } if (leaveCommandEncountered) break; } else { // Expecting Begin BLOCK_NAME; // if (token.Equals("BEGIN")) { token.SetEOFAllowed(false); token.ReadToken(); bool blockFound = false; NxsBlock * nxsBlockReader = NULL; // Look through all of the blocks readers that have been added to the NxsReader. If one responds true to // CanReadBlockType(), use it to read the block // for(ListOfBlockPtrs::iterator currIt = blockPtrs.begin(); !blockFound && currIt != blockPtrs.end(); ++currIt) { nxsBlockReader = *currIt; if(nxsBlockReader->CanReadBlockType(token.GetTokenReference())) blockFound = true; } // If we get the pointer from a weak pointer, this shared pointer will keep the block around while it reads the block NxsBlockPtrShared temp = NxsBlockPtrShared(); if (!blockFound) { for(ListOfWeakBlockPtrs::iterator currIt = weakBlockPtrs.begin(); currIt != weakBlockPtrs.end(); ++currIt) { temp = boost::make_shared(*currIt); nxsBlockReader = temp.get(); if(nxsBlockReader != NULL && nxsBlockReader->CanReadBlockType(token.GetTokenReference())) { blockFound = true; break; } } } int errCode = block_not_found; if (blockFound) { // A Block reader was found (or provided). Call UseNexusBlockToRead to read the info. // Return false for any fatal errors (disabled/unknown blocks are skipped and are // not treated as fatal errors) // assert(nxsBlockReader != NULL); errCode = UseNexusBlockToRead(nxsBlockReader, token); if (errCode == quit_or_leave) return true; if (errCode == early_eof || errCode == error_in_block) return false; } if(errCode != block_read ) { // Unknown block or disabled block // string currBlock = token.GetTokenReference(); try { if(errCode != disabled_block) SkippingBlock(currBlock); for(;;) { token.ReadToken(); if (token.Equals("END") || token.Equals("ENDBLOCK")) { token.ReadToken(); if(token.GetTokenReference() != ';') { errormsg << "Expecting ';' after END or ENDBLOCK command, but found " << token.GetTokenReference() << " instead"; NexusError(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn(), kCmdFailedGenerateMessage); return false; } break; } } } catch (NxsX_UnexpectedEOF & xeof) { xeof.msg << " in " << currBlock << " block."; NexusError(xeof.msg, xeof.pos, xeof.line, xeof.col, xeof.cResultCode, nxsBlockReader); return false; } } // if token not found amongst known block IDs token.SetEOFAllowed(true); } // if token equals BEGIN else if (token.Equals("#NEXUS") && ncl::NCL::allow(ncl::kNxsErrExtraPoundNexus)) { errormsg << "#NEXUS (extra #NEXUS statement at line " << token.GetFileLine() <<") "; this->SkippingBlock(errormsg); errormsg.clear(); } else { errormsg << "Expecting BEGIN and then a block name, but found " << token.GetTokenReference() << " instead"; NexusError(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn(), kCmdFailedGenerateMessage); return false; } } cmdCommentFound = token.ReadCommandCommentOrToken(); } // File completed (or [&LEAVE] command comment) without errors // if(notifyStartStop) ExecuteStopping(); return true; } /*---------------------------------------------------------------------------------------------------------------------- | This function is called when a NxsBlock (currBlock) is found to read the stream (token). | The function calls the following functions (bailing out if any of them return an error code): |> | NxsBlock:: IsEnabled(), | NxsReader::EnteringBlock(), | NxsBlock::Reset(), | NxsBlock::Read(), | NxsBlock::ExitRequested(), | NxsReader::ExitingBlock() |> | All `NxsException's are caught and dealt with (resulting in calls to NexusError()). | returns a value from the nxs_block_error_codes enum */ NxsReader::nxs_block_error_codes NxsReader::UseNexusBlockToRead( NxsBlock *currBlock, NxsToken &token) { if(!currBlock->IsEnabled()) { // hook to notify the user that a known, but disabled, block is being skipped // SkippingDisabledBlock(token.GetTokenReference()); return disabled_block; } if(!EnteringBlock(currBlock->GetID())) return could_not_enter; currBlock->Reset(); try { if (!currBlock->Read(token)) return error_in_block; // NOTE returning false from Read should mean that the error has been reported !! if (currBlock->ExitRequested()) return quit_or_leave; ExitingBlock(currBlock->GetID()); } catch (NxsX_UnexpectedEOF & xeof) { xeof.msg << " in " << currBlock->GetID() << " block."; NexusError(xeof.msg, xeof.pos, xeof.line, xeof.col, xeof.cResultCode, currBlock); currBlock->Reset(); return early_eof; } catch(NxsException & x) { string eMsg(x.msg); if (eMsg.empty()) eMsg = currBlock->GetErrorMessage(); if ((unsigned) x.pos == 0) NexusError(eMsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn(), x.cResultCode, currBlock); else NexusError(eMsg, x.pos, x.line, x.col, x.cResultCode, currBlock); currBlock->Reset(); return error_in_block; } return block_read; } #else //NEW_NXS_BLOCK_AND_READER #error "see code from revision 2049" #endif