This is BBMvtxFilter.m in view mode; [Download] [Up]
/* BBMvtxFilter.m * * This format filter does handle read/write of molecules in the * MoleViewer Text (.mvt) fileformat. * * For interface-info see the header file. The comments in this file mostly * cover only the real implementation details. * * Written by: Thomas Engel * Created: 15.03.1994 (Copyleft) * Last modified: 14.05.1994 */ #import "BBMvtxFilter.h" #import "BBFileFilterManager.h" #import "../BBAppManager.h" #import "../AtomLibrary.subproj/BBAtomLibraryManager.h" #import "../BBBeaker.h" #import "../BBMolecule.h" #import "../BBAtom.h" #import "../BBBond.h" #import <misckit/MiscString.h> #define MYBUFFERSIZE 500 #define COMMENTTOKEN "#" #define ELLIPSEDATATOKEN "ELLIPSE" #define SINGLEBONDTOKEN "S" #define DOUBLEBONDTOKEN "D" #define AROMATICBONDTOKEN "A" // I hate statics but here they might be useful. // The other variables have been added to the intercase to allow debugging. static char lineBuffer[MYBUFFERSIZE]; static FILE *inStream; @implementation BBMvtxFilter - (float)canReadFile:(const char*)filename { // Let's see if we can handle the file. Well this is a stupid check. // Right now we only check the file extention. If its "mvt" we // will do it. // Normally we should return (0, 0.5, 1) according to the percentage of // data we understand. More a fuzzy logic with MAYBE. id pathString; id aString; float answer; pathString = [MiscString new]; [pathString setPath:filename]; aString = [pathString fileExtension]; if( [aString casecmp:"mvt"] == 0 ) answer = (float)YES; else answer = (float)NO; [pathString free]; [aString free]; return answer; } - readBeaker:aBeaker fromFile:(const char*)filename { // A simple parser for MolViewer Textformat molecules. // The tokenList is only defined as long as its data is not already // parsed or it contains the parts that have to be parsed. Freeing // the list is not enough! We have to reset the id to nil to make the // check work properly. BOOL goAhead = YES; id pathString; id aString; inStream = fopen( filename, "r" ); if( inStream == NULL ) return self; // So lets parse the data get one line and do it. We will // not take care of all the settings! We'll say that the current line // is already parsed because we want another one to get read. mol = [BBMolecule new]; pathString = [MiscString new]; [pathString setPath:filename]; aString = [pathString fileBasename]; [(BBMolecule *)mol setName:[aString stringValue]]; [aString free]; [pathString free]; theLine = [MiscString new]; tokenList = nil; do { // Read a new line if no already done. Let the MOString create the // tokenList. We will only free it. if( !tokenList ) { if( !fgets( lineBuffer, MYBUFFERSIZE, inStream )) break; [theLine setStringValue:lineBuffer]; tokenList = [theLine tokenize:" \t\n\r" into:nil]; } // Now lets read till we have to stop. goAhead = [self readNewLine]; } while( goAhead ); [aBeaker addMolecule:mol]; [theLine free]; fclose( inStream ); return self; } - (BOOL)readNewLine { // Lets check the tokens..skip empty lines and comments // theLine and tokenList are already set according to our lineBuffer // If there is nothing to parse we want to see the next line.. // // This algorythm is stupid. It only checks for the two-token-line and // says that this is the line with the atom/bond counts. This will work // But is not really nice! // Although we check for ELLIPSE...we don't read the data. BOOL goAhead = YES; id token; int atomCount; int bondCount; token = [tokenList objectAt:0]; if( [tokenList count] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; } else if( [tokenList count] == 2 ) { // First we'll take the atom count and then we take the bond count. // If reading atoms was successful we will read the bonds. // We won't continue after that! atomCount = [[tokenList objectAt:0] intValue]; bondCount = [[tokenList objectAt:1] intValue]; [[tokenList freeObjects] free]; tokenList = nil; if( [self readAtomData:atomCount] ) [self readBondData:bondCount]; goAhead = NO; } else if( [token compareStr:ELLIPSEDATATOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; // We should read the ellipse info here. } // Currently we do not evaluate every possible token. // We will simple ingore everything else and just free the tokenList // if this has not already happened. else { [[tokenList freeObjects] free]; tokenList = nil; } return goAhead; } - (BOOL)readAtomData:(int)count { // Reading the atom data // Right here we will continue to read lines until count atoms have been // read. In normal MVTX Files the atoms have to follow without a break. BOOL goAhead = YES; id anAtom; id basicAtom; id atomLibrary; id token; atomLibrary = [[NXApp delegate] atomLibrary]; do { // Leave this place at files end or when there are no more atoms // to be read. if( count < 0 ) break; if( !fgets( lineBuffer, MYBUFFERSIZE, inStream )) { goAhead = NO; break; } [theLine setStringValue:lineBuffer]; tokenList = [theLine tokenize:" \t\n\r" into:nil]; // Check for comments and blank lines. // We won't accept less than 4 elements per line. token = [tokenList objectAt:0]; if( [tokenList count] < 4 || [token compareStr:COMMENTTOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; continue; } // Otherwise lets init a new atom from this line: // We have to find the basicAtom too. // To find the basic atom we need to convert the MolViewer atomtype // settings. if( [token compareStr:"C*"] == 0 || [token compareStr:"CA"] == 0 || [token compareStr:"CB"] == 0 || [token compareStr:"CC"] == 0 || [token compareStr:"CK"] == 0 || [token compareStr:"CM"] == 0 || [token compareStr:"CN"] == 0 || [token compareStr:"CQ"] == 0 || [token compareStr:"CR"] == 0 || [token compareStr:"CT"] == 0 || [token compareStr:"CV"] == 0 || [token compareStr:"CW"] == 0) [token setStringValue:"C"]; else if( [token compareStr:"H2"] == 0 || [token compareStr:"H3"] == 0 || [token compareStr:"HO"] == 0 || [token compareStr:"HS"] == 0 ) [token setStringValue:"H"]; else if( [token compareStr:"NM"] == 0 || [token compareStr:"N2"] == 0 || [token compareStr:"N3"] == 0 || [token compareStr:"N*"] == 0 || [token compareStr:"NA"] == 0 || [token compareStr:"NB"] == 0 || [token compareStr:"NC"] == 0 || [token compareStr:"NT"] == 0 ) [token setStringValue:"N"]; else if( [token compareStr:"O2"] == 0 || [token compareStr:"OS"] == 0 || [token compareStr:"OH"] == 0 ) [token setStringValue:"O"]; else if( [token compareStr:"SH"] == 0 ) [token setStringValue:"S"]; anAtom = [BBAtom new]; basicAtom = [atomLibrary findBasicAtomWithSymbol:[token stringValue]]; [anAtom setMotherAtom:basicAtom]; [anAtom setXPos:[[tokenList objectAt:1] doubleValue]]; [anAtom setYPos:[[tokenList objectAt:2] doubleValue]]; [anAtom setZPos:[[tokenList objectAt:3] doubleValue]]; [mol addAtom:anAtom]; --count; // Lets free the list and make the line as read. We don't need it // anymore. [[tokenList freeObjects] free]; tokenList = nil; } while( goAhead ); return goAhead; } - (BOOL)readBondData:(int)count { // Reading the bond data. // Right here we will continue to read lines until count bonds are read. BOOL goAhead = YES; BBBond * aBond; id atomList; id fromAtom; id toAtom; id token; char type; atomList = [mol atomList]; do { // We will leave this place when there are no more bonds to be read. // After that left read the new line and quite at files end. if( count < 1 ) break; if( !fgets( lineBuffer, MYBUFFERSIZE, inStream )) { goAhead = NO; break; } [theLine setStringValue:lineBuffer]; tokenList = [theLine tokenize:" \t\n\r" into:nil]; // Check for comments, blank lines and new tokens. // If there are not enough elements we will skip it token = [tokenList objectAt:0]; if( [tokenList count] < 3 || [token compareStr:COMMENTTOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; continue; } // otherwise lets init a new bond from this line: // We will set the name to some usefull value. aBond = [BBBond new]; token = [tokenList objectAt:2]; if( [token compareStr:SINGLEBONDTOKEN] == 0 ) type = BOND_SINGLE; else if( [token compareStr:DOUBLEBONDTOKEN] == 0 ) type = BOND_DOUBLE; else if( [token compareStr:AROMATICBONDTOKEN] == 0 ) type = BOND_DOUBLE_DELOCATED; else type = BOND_SINGLE; fromAtom = [atomList objectAt:[[tokenList objectAt:0] intValue]]; toAtom = [atomList objectAt:[[tokenList objectAt:1] intValue]]; [aBond connect:fromAtom to:toAtom with:type]; // We will use the token string object to compose our Bond name. [token setStringValue:[fromAtom symbol]]; [token catStringValue:"±"]; [token catStringValue:[toAtom symbol]]; [aBond setName:[token stringValue]]; [mol addBond:aBond]; --count; // Lets free the list and make the line as read. We don't need it // anymore [[tokenList freeObjects] free]; tokenList = nil; } while( goAhead ); return goAhead; } - writeBeaker:aBeaker toFile:(const char *)filename { return self; } @end /* * History: 30.10.94 Switched to the MiscString. * * 14.05.94 Changes to work with new BB... objects. * * 15.03.94 A basic implementation. * * * Bugs: - We could check the MVTX Magic word instead of relying on the file- * extension. * * - Does not handle Eliptic, charge and labeling info (15.03.94) */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.