/***************************************************************************** # Copyright (C) 1994-2008 by David Gordon. # All rights reserved. # # This software is part of a beta-test version of the Consed/Autofinish # package. It should not be redistributed or # used for any commercial purpose, including commercially funded # sequencing, without written permission from the author and the # University of Washington. # # This software is provided ``AS IS'' and any express or implied # warranties, including, but not limited to, the implied warranties of # merchantability and fitness for a particular purpose, are disclaimed. # In no event shall the authors or the University of Washington be # liable for any direct, indirect, incidental, special, exemplary, or # consequential damages (including, but not limited to, procurement of # substitute goods or services; loss of use, data, or profits; or # business interruption) however caused and on any theory of liability, # whether in contract, strict liability, or tort (including negligence # or otherwise) arising in any way out of the use of this software, even # if advised of the possibility of such damage. # # Building Consed from source is error prone and not simple which is # why I provide executables. Due to time limitations I cannot # provide any assistance in building Consed. Even if you do not # modify the source, you may introduce errors due to using a # different version of the compiler, a different version of motif, # different versions of other libraries than I used, etc. For this # reason, if you discover Consed bugs, I can only offer help with # those bugs if you first reproduce those bugs with an executable # provided by me--not an executable you have built. # # Modifying Consed is also difficult. Although Consed is modular, # some modules are used by many other modules. Thus making a change # in one place can have unforeseen effects on many other features. # It may takes months for you to notice these other side-effects # which may not seen connected at all. It is not feasable for me to # provide help with modifying Consed sources because of the # potentially huge amount of time involved. # #*****************************************************************************/ #include "rwcstring.h" #include "rwctokenizer.h" #include using namespace std; #include "please_wait.h" #include "filename.h" #include "gotoList.h" #include "guiMultiContigNavigator.h" #include "mbt_exception.h" #include "filePopupAndGetFilename.h" #include "consed.h" #include "contig.h" #include "locatedFragment.h" #include "bIsNumeric.h" #include "customNavigation.h" #include "soLine.h" static void parseSingleRegion( RWCString& soConsensusOrReadRegion, RWCString& soContig, RWCString& soRead, bool& bNext2AreUnpaddedReadPosInDirectionOfSequencing, int& nUnpaddedConsPosStart, int& nUnpaddedConsPosEnd, RWCString& soComment ); static FileName filCustomNavigation; static int nCurLine = 0; static FILE *pCustomNavigationFile; void askUserForCustomNavigationFileAndPopupWindowAndParseFile() { filCustomNavigation = ""; filCustomNavigation = filePopupAndGetFilename( "*.nav", "", "Select custom navigation file:" ); // case in which user pushed 'cancel' if ( filCustomNavigation.isNull() ) return; gotoList* pGotoList = new gotoList(); RWCString soTitle; PleaseWait* pPleaseWait; try { pPleaseWait = new PleaseWait( GuiApp::pGetGuiApp()->widGetTopLevel() ); readCustomNavigationFile( filCustomNavigation, soTitle, pGotoList ); delete pPleaseWait; } catch(ExceptionBase eb) { // this is one of ours, report it if not done so if (! eb.bUserNotified() ) { GuiApp::popupErrorMessage(eb.szGetDesc()); } delete pPleaseWait; delete pGotoList; fclose( pCustomNavigationFile ); return; } guiMultiContigNavigator* pGui = new guiMultiContigNavigator( soTitle, soTitle ); ConsEd::pGetConsEd()->addGuiMultiContigNavigator( pGui ); for( int n = 0; n < pGotoList->nGetNumGotoItems(); ++n ) { gotoItem* pGotoItem = pGotoList->pGetGotoItem( n ); pGui->appendToMultiContigNavigator( pGotoItem ); } } #define PARSE_ERROR( szMessage ) \ { ostringstream ost; \ ost << "Error detected from source file " \ << __FILE__ << " at " << __LINE__ < aContigsAlreadySetUnpaddedPositionsArray( ConsEd::pGetAssembly()->nNumContigs(), "aContigsAlreadySetUnpaddedPositionsArray" ); pCustomNavigationFile = 0; pCustomNavigationFile = fopen( filCustomNavigation.data(), "r" ); // file open? if (!pCustomNavigationFile ) { ostringstream ost; ost << "Unable to open file " << filCustomNavigation << endl << ends; SysRequestFailed srf(ost.str().c_str()); srf.includeErrnoDescription(); throw srf; } // count items in order to resize pGotoList int nNumberOfRegions = 0; while( bGetNextLine() ) { if ( soLine == "BEGIN_REGION" ) ++nNumberOfRegions; } rewind( pCustomNavigationFile ); nCurLine = 0; pGotoList->resizeGotoList( nNumberOfRegions ); // look for TITLE: if (! bGetNextLine() ) PARSE_ERROR( "Premature end of file" ); RWCTokenizer tokLine( soLine ); RWCString soTitleLabel = tokLine(); if ( soTitleLabel != "TITLE:" ) PARSE_ERROR( "Are you sure this is a navigation file? The first line in a navigation file must start with TITLE:" ); soTitle = ""; if ( soLine.length() >= 8 ) { soTitle = soLine( 7, soLine.length() - 7 ); } bool bUserDefinedPositions = soTitle.bContains( "user-defined positions", cCaseInsensitive ); cerr << "using user-defined positions: " << szPrintBool( bUserDefinedPositions ) << endl; // now looking for: // BEGIN_REGION // TYPE: CONSENSUS // CONTIG: Contig1 // UNPADDED_CONS_POS: 1 5 // COMMENT: This is a comment // END_REGION // the only acceptable options are whitespace, BEGIN_REGION, or EOF bool bFoundARegion = false; while( true ) { bool bWhitespace = true; while( bWhitespace ) { if ( !bGetNextLine() ) { if (!bFoundARegion ) GuiApp::popupErrorMessage( "There were no regions in the custom navigation file." ); fclose( pCustomNavigationFile ); return; } else if (! soLine.bIsWhitespace() ) { bWhitespace = false; } // otherwise it is whitespace so get another line } if (soLine != "BEGIN_REGION" ) PARSE_ERROR( "Are you sure this is navigation file? At this point there should be BEGIN_REGION" ); RWCString soConsensusOrReadRegion; RWCString soContig; RWCString soRead; bool bNext2AreUnpaddedReadPos; int nUnpaddedConsPosStart; int nUnpaddedConsPosEnd; RWCString soComment; parseSingleRegion( soConsensusOrReadRegion, soContig, soRead, bNext2AreUnpaddedReadPos, nUnpaddedConsPosStart, nUnpaddedConsPosEnd, soComment ); bFoundARegion = true; Contig* pContig = ConsEd::pGetAssembly()->pGetContigByName( soContig ); if (!pContig && soConsensusOrReadRegion == "CONSENSUS" ) { char szError[200]; sprintf( szError, "Could not find contig %s", (char*) soContig.data() ); PARSE_ERROR( szError ); } LocatedFragment* pLocFrag = 0; if ( soConsensusOrReadRegion == "READ" ) { pLocFrag = ConsEd::pGetAssembly()->pGetLocatedFragmentByName( soRead ); if ( !pLocFrag ) { RWCString soError = "Could not find read "; soError += soRead; soError += " in contig "; soError += soContig; PARSE_ERROR( soError ); } pContig = pLocFrag->pGetContig(); } int nUnpaddedStartContigConsPos; int nUnpaddedEndContigConsPos; // don't call setUnpaddedPositionsArray every time (DG, June 2009) if ( !aContigsAlreadySetUnpaddedPositionsArray.bContains( pContig ) ) { // needed by nUnpaddedIndex pContig->setUnpaddedPositionsArray(); aContigsAlreadySetUnpaddedPositionsArray.insert( pContig ); } if ( bNext2AreUnpaddedReadPos ) { int nUnpadded1 = pLocFrag->nUnpaddedConsPosFromOrientedUnpaddedFragPos( nUnpaddedConsPosStart ); int nUnpadded2 = pLocFrag->nUnpaddedConsPosFromOrientedUnpaddedFragPos( nUnpaddedConsPosEnd ); nUnpaddedConsPosStart = MIN( nUnpadded1, nUnpadded2 ); nUnpaddedConsPosEnd = MAX( nUnpadded1, nUnpadded2 ); } pContig->setStartNumberingUnpaddedConsensusAtUserDefinedIfNecessary(); if ( bUserDefinedPositions ) { if ( soConsensusOrReadRegion == "READ" ) { nUnpaddedStartContigConsPos = pContig->nUnpaddedIndexStartingAtUserDefinedPosition2( pContig->nGetFirstDisplayableContigPos() ); nUnpaddedEndContigConsPos = pContig->nUnpaddedIndexStartingAtUserDefinedPosition2( pContig->nGetLastDisplayableContigPos() ); } else { nUnpaddedStartContigConsPos = pContig->nUnpaddedIndexStartingAtUserDefinedPosition2( pContig->nGetStartConsensusIndex() ); nUnpaddedEndContigConsPos = pContig->nUnpaddedIndexStartingAtUserDefinedPosition2( pContig->nGetEndConsensusIndex() ); } } else { if ( soConsensusOrReadRegion == "READ" ) { nUnpaddedStartContigConsPos = pContig->nUnpaddedIndex( pContig->nGetFirstDisplayableContigPos() ); nUnpaddedEndContigConsPos = pContig->nUnpaddedIndex( pContig->nGetLastDisplayableContigPos() ); } else { nUnpaddedStartContigConsPos = pContig->nUnpaddedIndex( pContig->nGetStartConsensusIndex() ); nUnpaddedEndContigConsPos = pContig->nUnpaddedIndex( pContig->nGetEndConsensusIndex() ); } } if (! ( (nUnpaddedStartContigConsPos <= nUnpaddedConsPosStart) && (nUnpaddedConsPosStart <= nUnpaddedConsPosEnd ) && (nUnpaddedConsPosEnd <= nUnpaddedEndContigConsPos ) )) { RWCString soErrorMessage( (size_t) 1000 ); soErrorMessage.nCurrentLength_ = sprintf( soErrorMessage.data(), "Range of region (%d to %d) must be in order and within the range of the contig (%d to %d) %s", nUnpaddedConsPosStart, nUnpaddedConsPosEnd, nUnpaddedStartContigConsPos, nUnpaddedEndContigConsPos, pContig->soGetName().data() ); if ( bUserDefinedPositions ) { soErrorMessage += " trying to use user-defined positions from tag "; } soErrorMessage += " and contig starts at "; if ( pContig->nStartNumberingUnpaddedConsensusAtUserDefined_ != 1 ) { soErrorMessage += "(user-defined) "; } soErrorMessage += RWCString( (long) pContig->nStartNumberingUnpaddedConsensusAtUserDefined_ ); if ( !bUserDefinedPositions && pContig->nStartNumberingUnpaddedConsensusAtUserDefined_ != 1 ) { soErrorMessage += ". Put \"(user-defined positions)\" in the title of the .nav file if you want to use user-defined consensus positions rather than 1-based consensus positions."; } THROW_ERROR( soErrorMessage ); } // now convert nUnpaddedConsPosStart and nUnpaddedConsPosEnd to // 1-based positions if ( bUserDefinedPositions ) { if ( !pContig->bSetStartNumberingUnpaddedConsensusAtUserDefined_ ) pContig->setStartNumberingUnpaddedConsensusAtUserDefinedIfNecessary(); nUnpaddedConsPosStart -= ( pContig->nStartNumberingUnpaddedConsensusAtUserDefined_ - 1 ); nUnpaddedConsPosEnd -= ( pContig->nStartNumberingUnpaddedConsensusAtUserDefined_ - 1 ); } // changed from nPaddedIndex to nPaddedIndexFast for performance // (DG, June 2009) int nPaddedConsPosStart = pContig->nPaddedIndexFast( nUnpaddedConsPosStart ); int nPaddedConsPosEnd = pContig->nPaddedIndexFast( nUnpaddedConsPosEnd ); if ( soConsensusOrReadRegion == "READ" ) { if ( !( ( pLocFrag->nGetAlignStart() <= nPaddedConsPosStart ) && ( nPaddedConsPosStart <= nPaddedConsPosEnd ) && ( nPaddedConsPosEnd <= pLocFrag->nGetAlignEnd() ) )) { GuiApp::popupErrorMessage( "Range of the region (%d to %d in padded coordinates) of read %s must be in order and lie within the range of the read (%d to %d)", nPaddedConsPosStart, nPaddedConsPosEnd, pLocFrag->soGetName().data(), pLocFrag->nGetAlignStart(), pLocFrag->nGetAlignEnd() ); THROW_ERROR_CN(); } } gotoItem* pGotoItem = new gotoItem( pContig, pLocFrag, nPaddedConsPosStart, nPaddedConsPosEnd, nUnpaddedConsPosStart, nUnpaddedConsPosEnd, soComment ); pGotoList->addToList( pGotoItem); } // while( true ) { fclose( pCustomNavigationFile ); } void parseSingleRegion( RWCString& soConsensusOrReadRegion, RWCString& soContig, RWCString& soRead, bool& bNext2AreUnpaddedReadPosInDirectionOfSequencing, int& nUnpaddedConsPosStart, int& nUnpaddedConsPosEnd, RWCString& soComment ) { // BEGIN_REGION // TYPE: CONSENSUS // CONTIG: Contig1 // UNPADDED_CONS_POS: 1 5 // COMMENT: This is a comment // END_REGION // now looking for TYPE: if ( !bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for TYPE:" ); RWCTokenizer tokType( soLine ); RWCString soTypeLineToken = tokType(); if ( soTypeLineToken != "TYPE:" ) PARSE_ERROR( "Expected this line to start with TYPE:" ); soConsensusOrReadRegion = tokType(); soConsensusOrReadRegion.toUpper(); if (soConsensusOrReadRegion != "CONSENSUS" && soConsensusOrReadRegion != "READ" ) PARSE_ERROR( "Expected this line to be TYPE: CONSENSUS or TYPE: READ" ); if ( !bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for CONTIG:" ); RWCTokenizer tokContig( soLine ); RWCString soContigLabel = tokContig(); bool bPutBackLineForReadType = false; if ( soContigLabel != "CONTIG:" ) { if ( soConsensusOrReadRegion == "CONSENSUS" ) { PARSE_ERROR( "Expected this line to start with CONTIG:" ); } else { soContig = "(not specified)"; bPutBackLineForReadType = true; } } else { soContig = tokContig(); } if ( soConsensusOrReadRegion == "READ" ) { if ( !bPutBackLineForReadType ) { if (!bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for READ:" ); } RWCTokenizer tokRead( soLine ); RWCString soReadLabel = tokRead(); if (soReadLabel != "READ:" ) PARSE_ERROR( "Expected this line to start with READ:" ); soRead = tokRead(); } if ( !bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for UNPADDED_CONS_POS:" ); RWCTokenizer tokUnpaddedConsPos( soLine ); RWCString soUnpaddedConsPosLabel = tokUnpaddedConsPos(); if (soUnpaddedConsPosLabel == "UNPADDED_CONS_POS:" ) { bNext2AreUnpaddedReadPosInDirectionOfSequencing = false; } else if (soUnpaddedConsPosLabel == "UNPADDED_READ_POS:" ) { bNext2AreUnpaddedReadPosInDirectionOfSequencing = true; } else { PARSE_ERROR( "Expected this line to start with UNPADDED_CONS_POS: or UNPADDED_READ_POS:" ); } if ( soConsensusOrReadRegion == "CONSENSUS" && bNext2AreUnpaddedReadPosInDirectionOfSequencing ) { PARSE_ERROR( "TYPE: CONSENSUS cannot be used with UNPADDED_READ_POS: only with UNPADDED_CONS_POS:" ); } RWCString soUnpaddedConsPosStart = tokUnpaddedConsPos(); RWCString soUnpaddedConsPosEnd = tokUnpaddedConsPos(); if (soUnpaddedConsPosStart.isNull() || soUnpaddedConsPosEnd.isNull() ) PARSE_ERROR( "UNPADDED_CONS_POS: must be followed by the start and end of the region" ); if ( !bIsNumeric( soUnpaddedConsPosStart ) || !bIsNumeric( soUnpaddedConsPosEnd ) ) PARSE_ERROR( "UNPADDED_CONS_POS: must be followed by two numbers--the start and end of the region" ); nUnpaddedConsPosStart = atoi( (char*) soUnpaddedConsPosStart.data() ); nUnpaddedConsPosEnd = atoi( (char*) soUnpaddedConsPosEnd.data() ); if ( !bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for COMMENT:" ); RWCTokenizer tokComment( soLine ); RWCString soCommentLabel = tokComment(); if (soCommentLabel != "COMMENT:" ) PARSE_ERROR( "Expected this line to start with COMMENT:" ); if ( soLine.length() < 10 ) soComment = ""; else { soComment = soLine( 9, soLine.length() - 9 ); soComment = soComment.strip( RWCString::LEADING ); } // now read lines until see END_REGION bool bFoundEndRegion = false; while( !bFoundEndRegion ) { if (!bGetNextLine() ) PARSE_ERROR( "premature end of file while looking for END_REGION" ); RWCTokenizer tokEndRegion( soLine ); RWCString soEndRegionLabel = tokEndRegion(); if ( soEndRegionLabel == "END_REGION" ) bFoundEndRegion = true; } }