This is BBPdbFilter.m in view mode; [Download] [Up]
/* BBPdbFilter.m * * This format filter does handle read/write of molecules in the * Protein Data Bank (.pdb or .PDB) 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: 01.11.1994 (Copyleft) * Last modified: 01.11.1994 */ #import "BBPdbFilter.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 SINGLEBONDTOKEN "E" #define SINGLEFIXEDBONDTOKEN "Ef" #define DOUBLEBONDTOKEN "D" #define TRIPLEBONDTOKEN "T" #define DOUBLEDELOCATEDBONDTOKEN "Dd" // 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 BBPdbFilter - (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 "pdb" or "PDB" we // will do it. // Normally we should return (0, 0.5, 1) according to the percentage of // data we understand. id pathString; id aString; float answer; pathString = [MiscString new]; [pathString setPath:filename]; aString = [pathString fileExtension]; if( [aString casecmp:"pdb"] == 0 ) answer = (float)YES; else answer = (float)NO; [pathString free]; [aString free]; return answer; } - readBeaker:aBeaker fromFile:(const char*)filename { // A simple parser for Protein Data Bank Molecules. // The tokenList is only defined as long as its data is not allready // 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; 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 wan another one to get read. mol = [BBMolecule new]; theLine = [MiscString new]; tokenList = nil; do { // Read a new line if no already done. Let the MiscString 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 acording to our lineBuffer // If there is nothing to parse we want to see the next line.. BOOL goAhead = YES; id token; id aString; token = [tokenList objectAt:0]; if( [tokenList count] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; } else if( [token compareStr:"COMPND"] == 0 ) { aString = [theLine substringFrom:11 to:70]; [aString trimTailWhiteSpaces]; [(BBMolecule *)mol setName:[aString stringValue]]; [aString free]; [[tokenList freeObjects] free]; tokenList = nil; } else if( [token compareStr:"ATOM"] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; goAhead = [self readAtomData]; } else if( [token compareStr:"HETATM"] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; goAhead = [self readAtomData]; } else if( [token compareStr:"CONECT"] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; // goAhead = [self readBondData]; } // 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 { // Reading the atom data // Right here we will continue to read lines until a new token comes. // As a token we will take everything that does not match a atom definition id anAtom; id basicAtom; id atomLibrary; id string; id symbol; atomLibrary = [[NXApp delegate] atomLibrary]; // Well now let's check the single details of the current theLine... // the ATOM entries.. symbol = [theLine substringFrom:13 to:16]; [symbol trimTailWhiteSpaces]; string = [MiscString new]; [self checkSymbol:symbol andUpdate:string]; // We have to find the basicAtom too. anAtom = [BBAtom new]; basicAtom = [atomLibrary findBasicAtomWithSymbol:[symbol stringValue]]; [anAtom setMotherAtom:basicAtom]; [anAtom setSymbolExtention:[string stringValue]]; [string free]; [symbol free]; string = [theLine substringFrom:31 to:38]; [string trimTailWhiteSpaces]; [anAtom setXPos:[string doubleValue]]; [string free]; string = [theLine substringFrom:39 to:46]; [string trimTailWhiteSpaces]; [anAtom setYPos:[string doubleValue]]; [string free]; string = [theLine substringFrom:47 to:54]; [string trimTailWhiteSpaces]; [anAtom setZPos:[string doubleValue]]; [string free]; [mol addAtom:anAtom]; return YES; } - (BOOL)readBondData { // Reading the bond data. // Right here we will continue to read lines until a new token comes. // As a token we will take everything that does not match a bond definition BOOL goAhead = YES; BBBond * aBond; id atomList; id fromAtom; id toAtom; id token; char type; atomList = [mol atomList]; // Check for comments, blank lines and new tokens. // If there are not enough elements it has to be a token. // Well kind of stupid but it should work for every LOOK mol // otherwise lets init a new bond from this line: // We will set the name to some usefull value. aBond = [BBBond new]; if( [token compareStr:SINGLEBONDTOKEN] == 0 ) type = BOND_SINGLE; else if( [token compareStr:SINGLEFIXEDBONDTOKEN] == 0 ) type = BOND_SINGLE; else if( [token compareStr:DOUBLEBONDTOKEN] == 0 ) type = BOND_DOUBLE; else if( [token compareStr:TRIPLEBONDTOKEN] == 0 ) type = BOND_TRIPLE; else if( [token compareStr:DOUBLEDELOCATEDBONDTOKEN] == 0 ) type = BOND_DOUBLE_DELOCATED; else type = BOND_SINGLE; fromAtom = [atomList objectAt:[[tokenList objectAt:1] intValue]]; toAtom = [atomList objectAt:[[tokenList objectAt:2] 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]; // Lets free the list and make the line as read. We don't need it // anymore [[tokenList freeObjects] free]; tokenList = nil; return YES; } - checkSymbol:symbol andUpdate:symbolExtention { // PDB symbol contain counts and special symbols at their end. // We will move them to the symbolExtentions. int i; id aString; for( i=0; i<[symbol length]; i++ ) if( [symbol charAt:i] < 'A' || [symbol charAt:i] > 'z' || ([symbol charAt:i] < 'a' && i > 0 )) break; aString = [symbol substringFrom:i to:[symbol length]-1]; [symbolExtention setStringValue:[aString stringValue]]; [aString free]; 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: 01.11.94 Created this object to parse ATOm and BOND info only. * * * Bugs: - many PDB marks not supported */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.