This is BBMacMoleculeFilter.m in view mode; [Download] [Up]
/* BBMacMoleculeFilter.m * * This format filter does handle read/write of molecules in the * MacMolecule Text (.macMol) 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: 25.03.1994 (Copyleft) * Last modified: 30.10.1994 */ #import "BBMacMoleculeFilter.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 ";" // 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 BBMacMoleculeFilter - (float)canReadFile:(const char*)filename { // Let's see if we can handle the file. We will check the extention. // "macMol" will be regonized. id pathString; id aString; float answer; pathString = [MiscString new]; [pathString setPath:filename]; aString = [pathString fileExtension]; if( [aString casecmp:"macMol"] == 0 ) answer = (float)YES; else answer = (float)NO; [pathString free]; [aString free]; return answer; } - readBeaker:aBeaker fromFile:(const char*)filename { // A simple parser for MacMolecule 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. // Right here we only trigger reading the atoms and bonds. I hope that // they will always come in that order. id aString; id pathString; inStream = fopen( filename, "r" ); if( inStream == NULL ) return self; // We won't take care of any tokens right here. Just set the molname from // the filename and init the list for tracking the atom symbols to allow // setting up the bonds. // The basic toke mechanism is still in here to allow easy token parsing.. // if the format does define them. mol = [BBMolecule new]; symbolList = [List new]; theLine = [MiscString new]; tokenList = nil; // The file has no info on the name. So we will take the filename. pathString = [[MiscString new] setPath:filename]; aString = [pathString fileBasename]; [(BBMolecule *)mol setName:[aString stringValue]]; [aString free]; [pathString free]; /* if( fgets( lineBuffer, MYBUFFERSIZE, inStream )) { [theLine setStringValue:lineBuffer]; tokenList = [theLine tokenize:" \t\n\r" into:nil]; atomCount = [[tokenList objectAt:0] intValue]; bondCount = [[tokenList objectAt:2] intValue]; aString = [theLine substringFromToken:6 toToken:[tokenList count]-1 ofList:tokenList]; [(BBMolecule *)mol setName:[aString stringValue]]; [aString free]; [[tokenList freeObjects] free]; tokenList = nil; if( [self readAtomData:atomCount] ) [self readBondData:bondCount]; } */ if( [self readAtomData] ) [self readBondData]; [aBeaker addMolecule:mol]; [theLine free]; [[symbolList freeObjects] free]; fclose( inStream ); return self; } - (BOOL)readAtomData { // Reading the atom data // Here we will set up the temp list for the bond stuff. // The recognizion is very stupid. Every line has to match a pattern. BOOL goAhead = YES; id anAtom; id basicAtom; id atomLibrary; id token; atomLibrary = [[NXApp delegate] atomLibrary]; do { if( !tokenList ) { 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 6 elements per line. // If we allready did read a atom and we have 2 token in the list // We should stop. This will be the first bond. if( [tokenList count] == 2 && [symbolList count] > 0 ) break; token = [tokenList objectAt:0]; if( [tokenList count] < 5 || [[tokenList objectAt:1] compareStr:":"] != 0 || [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 MacMolecule atom // symbol. Therefore we strip of any unwanted characters. // We will add the original symbol at the atom to the lists so we can // find them when reading the bonds. anAtom = [BBAtom new]; [symbolList addObject:[token copy]]; [self checkSymbol:token]; basicAtom = [atomLibrary findBasicAtomWithSymbol:[token stringValue]]; [anAtom setMotherAtom:basicAtom]; [anAtom setXPos:[[tokenList objectAt:2] doubleValue] / 10]; [anAtom setYPos:[[tokenList objectAt:3] doubleValue] / 10]; [anAtom setZPos:[[tokenList objectAt:4] doubleValue] / 10]; [mol addAtom:anAtom]; // 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 { // Reading the bond data. // We will use the tempLists to find the right atoms to be connected. // MacMolecule has no infos on the bondtype so everything will be a // single bond. BOOL goAhead = YES; BBBond * aBond; int i; id atomList; id fromAtom; id toAtom; id token; char type; atomList = [mol atomList]; do { if( !tokenList ) { 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] < 2 || [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. // From the first atom symbol we have to strip the last "," // Then we will search the symbolList. // We always work with the premise that a molecule adds all the // atom objects in the same order to its internal atomList as we // tell it to. aBond = [BBBond new]; type = BOND_SINGLE; token = [[tokenList objectAt:0] substringFrom:0 to:[[tokenList objectAt:0] length]-2 ]; for( i=0; i<[symbolList count]; i++ ) if( [[symbolList objectAt:i] compare:token] == 0 ) break; fromAtom = [atomList objectAt:i]; token = [tokenList objectAt:1]; for( i=0; i<[symbolList count]; i++ ) if( [[symbolList objectAt:i] compare:token] == 0 ) break; toAtom = [atomList objectAt:i]; [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]; // 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; } - checkSymbol:symbol { // MacMolecule symbol contain counts at their end. // We will remove them here. int i; id aString; for( i=0; i<[symbol length]; i++ ) if( [symbol charAt:i] < 'A' || [symbol charAt:i] > 'z' ) break; aString = [symbol substringFrom:0 to:i-1]; [symbol setStringValue:[aString stringValue]]; [aString free]; return self; } - 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 will watch for '#' comment even they are not defined for that * data format.. * * - Does not handle charges and strips extra data from the symbol * name. These symbolextentions may be automatically typed to our * symbol addition area..perhaps with some conversion ?? (15.03.94) */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.