This is BBLook3DFilter.m in view mode; [Download] [Up]
/* BBLook3DFilter.m * * This format filter does handle read/write of molecules in the * LOOK3D (.lookMol) 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: 09.03.1994 (Copyleft) * Last modified: 30.10.1994 */ #import "BBLook3DFilter.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 MOLNAMETOKEN "@molname" #define MOLCOLORTOKEN "@molcolor" #define ATOMDATATOKEN "@atomdata" #define BONDDATATOKEN "@binddata" #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 BBLook3DFilter - (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 "lookMol" 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:"lookMol"] == 0 ) answer = (float)YES; else answer = (float)NO; [pathString free]; [aString free]; return answer; } - readBeaker:aBeaker fromFile:(const char*)filename { // A simple parser for LOOK 3D 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:MOLNAMETOKEN] == 0 ) { aString = [theLine substringFromToken:1 toToken:[tokenList count]-1 ofList:tokenList]; [(BBMolecule *)mol setName:[aString stringValue]]; [aString free]; [[tokenList freeObjects] free]; tokenList = nil; } else if( [token compareStr:MOLCOLORTOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; } else if( [token compareStr:ATOMDATATOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; goAhead = [self readAtomData]; } else if( [token compareStr:BONDDATATOKEN] == 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 BOOL goAhead = YES; id anAtom; id basicAtom; id atomLibrary; id token; atomLibrary = [[NXApp delegate] atomLibrary]; do { 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 it has to be a token. // Well kind of stupid but it should work for every LOOK mol token = [tokenList objectAt:0]; if( [tokenList count] == 0 || [token compareStr:COMMENTTOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; continue; } else if( [tokenList count] != 4 ) break; // otherwise lets init a new atom from this line: // We have to find the basicAtom too. 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]; // 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. // 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]; do { 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 it has to be a token. // Well kind of stupid but it should work for every LOOK mol token = [tokenList objectAt:0]; if( [tokenList count] == 0 || [token compareStr:COMMENTTOKEN] == 0 ) { [[tokenList freeObjects] free]; tokenList = nil; continue; } else if( [tokenList count] != 3 ) break; // 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; } 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. * * 14.03.94 First 'real' filter of this project. Extracted form the * old FileFormatManager * * 14.01.94 Made the parsing somewhat nicer and cleaner. * * 13.01.94 Did completely rewrite the parsing section. Now uses * MOStrings to parse self. * * 28.12.93 Created a initBeaker method. * * * Bugs: - Blank lines inside a atom/bond data block will stop the parsing * and comments will do the same. This might be implemented some day. */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.