// // BCSequenceDNABase.m // BioCocoa // // Created by John Timmer on 8/11/04. // Copyright 2004 John Timmer. All rights reserved. // #import "BCSequenceDNABase.h" static BCSequenceDNABase *adenosineRepresentation = nil; static BCSequenceDNABase *thymidineRepresentation = nil; static BCSequenceDNABase *cytidineRepresentation = nil; static BCSequenceDNABase *guanidineRepresentation = nil; static BCSequenceDNABase *anyBaseRepresentation = nil; static NSMutableDictionary *customBases = nil; @implementation BCSequenceDNABase #pragma mark â CLASS METHOS //////////////////////////////////////////////////////////////////////////// // THIS METHOD CREATES THE SINGLETON REFERENCES TO ALL THE STANDARD BASES //////////////////////////////////////////////////////////////////////////// + (void) initBases { // FIND OUR BUNDLE AND LOAD UP THE BASE DEFINITIONS NSBundle *biococoaBundle = [NSBundle bundleForClass: [BCSequenceDNABase class]]; NSString *filePath = [biococoaBundle pathForResource: @"base template" ofType: @"plist"]; if ( filePath == nil ) return; NSMutableDictionary *baseDefinitions = [NSMutableDictionary dictionaryWithContentsOfFile: filePath]; if ( baseDefinitions == nil ) return; // GO THROUGH AND CREATE EACH SINGLETON BASE DEFINITION, USING THE DICTIONARY NSDictionary *tempDict = [baseDefinitions objectForKey: @"adenosine"]; if ( tempDict != nil && [tempDict isKindOfClass: [NSDictionary class]] ) { adenosineRepresentation = [[BCSequenceDNABase alloc] initWithDictionary: tempDict]; [baseDefinitions removeObjectForKey: @"adenosine"]; } tempDict = [baseDefinitions objectForKey: @"thymidine"]; if ( tempDict != nil && [tempDict isKindOfClass: [NSDictionary class]] ) { thymidineRepresentation = [[BCSequenceDNABase alloc] initWithDictionary: tempDict]; [baseDefinitions removeObjectForKey: @"thymidine"]; } tempDict = [baseDefinitions objectForKey: @"cytidine"]; if ( tempDict != nil && [tempDict isKindOfClass: [NSDictionary class]] ) { cytidineRepresentation = [[BCSequenceDNABase alloc] initWithDictionary: tempDict]; [baseDefinitions removeObjectForKey: @"cytidine"]; } tempDict = [baseDefinitions objectForKey: @"guanidine"]; if ( tempDict != nil && [tempDict isKindOfClass: [NSDictionary class]] ) { guanidineRepresentation = [[BCSequenceDNABase alloc] initWithDictionary: tempDict]; [baseDefinitions removeObjectForKey: @"guanidine"]; } tempDict = [baseDefinitions objectForKey: @"any base"]; if ( tempDict != nil && [tempDict isKindOfClass: [NSDictionary class]] ) { anyBaseRepresentation = [[BCSequenceDNABase alloc] initWithDictionary: tempDict]; [baseDefinitions removeObjectForKey: @"any base"]; } // hang on to the dictionary, in case there are custom bases customBases = [baseDefinitions retain]; } //////////////////////////////////////////////////////////////////////////// // THE FOLLOWING IS A METHOD FOR OBTAINING REFERENCES TO THE // INDIVIDUAL BASE REPRESENTATIONS WHEN GIVEN A SINGLE LETTER CODE // // THIS WILL NOT WORK WITH CUSTOM BASES, SINCE THEIR SYMBOLS ARE NOT KNOWN IN ADVACE //////////////////////////////////////////////////////////////////////////// + (id) baseForSymbol: (unichar)entry { switch ( entry ) { case 'A' : { return [BCSequenceDNABase adenosine]; break; } case 'T' : { return [BCSequenceDNABase thymidine]; break; } case 'C' : { return [BCSequenceDNABase cytidine]; break; } case 'G' : { return [BCSequenceDNABase guanidine]; break; } case 'N' : { return [BCSequenceDNABase anyBase]; break; } default : return nil; } } //////////////////////////////////////////////////////////////////////////// // THE FOLLOWING ARE METHODS FOR OBTAINING REFERENCES TO THE // INDIVIDUAL BASE REPRESENTATIONS //////////////////////////////////////////////////////////////////////////// + (BCSequenceDNABase *) adenosine { if ( adenosineRepresentation == nil ) [BCSequenceDNABase initBases]; return adenosineRepresentation; } + (BCSequenceDNABase *) thymidine { if ( thymidineRepresentation == nil ) [BCSequenceDNABase initBases]; return thymidineRepresentation; } + (BCSequenceDNABase *) cytidine { if ( cytidineRepresentation == nil ) [BCSequenceDNABase initBases]; return cytidineRepresentation; } + (BCSequenceDNABase *) guanidine { if ( guanidineRepresentation == nil ) [BCSequenceDNABase initBases]; return guanidineRepresentation; } + (BCSequenceDNABase *) anyBase { if ( anyBaseRepresentation == nil ) [BCSequenceDNABase initBases]; return anyBaseRepresentation; } + (BCSequenceDNABase *) customBase: (NSString *)baseName { if ( customBases == nil ) [BCSequenceDNABase initBases]; id aBase = [customBases objectForKey: baseName]; if ( aBase == nil) return nil; if ( [aBase isKindOfClass: [BCSequenceDNABase class]] ) return aBase; if ( [aBase isKindOfClass: [NSDictionary class]] ) { aBase = [[[BCSequenceDNABase alloc] initWithDictionary: aBase] autorelease]; if ( aBase != nil ) { [customBases setObject: aBase forKey: baseName]; return aBase; } } return nil; } //////////////////////////////////////////////////////////////////////////// // OBJECT METHODS //////////////////////////////////////////////////////////////////////////// #pragma mark â #pragma mark â OBJECT METHODS #pragma mark â #pragma mark âINITIALIZATION METHODS - (BCSequenceDNABase *) initWithDictionary: (NSDictionary *)entry { self = [super init]; if ( self == nil ) return nil; // we hang onto the dictionary in order to establish complement realtionships // once all the bases are generated baseInfo = [entry copy]; // get basic information about this base name = [baseInfo objectForKey: @"Name"]; if (name == nil) return nil; else [name retain]; symbolString = [baseInfo objectForKey: @"Symbol"]; if (symbolString == nil || [symbolString length] == 0) return nil; else [symbolString retain]; symbol = [symbolString characterAtIndex: 0]; NSNumber *tempNumber = [baseInfo objectForKey: @"single base"]; if (tempNumber == nil) return nil; isSingleBase = [tempNumber boolValue]; // nil out the base relationships so that we can detect // that they still need to be initialized complement = nil; complements = nil; represents = nil; representedBy = nil; return self; } - (void) initializeBaseRelationships { // THIS METHOD IS CALLED AFTER OBJECT INITIALIZATION BECAUSE IT // REQUIRES THE EXISTENCE OF ALL THE OTHER BASES IN ORDER TO WORK // IT SHOULD BE CALLED THE FIRST TIME ONE OF THESE OBJECTS IS NEEDED NSString *baseReference = [baseInfo objectForKey: @"Complement"]; if ( baseReference == nil || [baseReference length] == 0 ) return; complement = [BCSequenceDNABase performSelector: NSSelectorFromString( baseReference )]; if (complement == nil) return; NSArray *infoArray = [baseInfo objectForKey: @"All Complements"]; if ( infoArray == nil || [infoArray count] == 0 ) return; NSEnumerator *objectEnumerator = [infoArray objectEnumerator]; NSMutableArray *tempArray = [NSMutableArray array]; BCSequenceDNABase *tempBase; while ( baseReference = [objectEnumerator nextObject] ) { if ( baseReference == nil || [baseReference length] == 0 ) return; tempBase = [BCSequenceDNABase performSelector: NSSelectorFromString( baseReference )]; if ( tempBase != nil ) [tempArray addObject: tempBase]; else return; } complements = [tempArray copy]; infoArray = [baseInfo objectForKey: @"Represents"]; if ( infoArray == nil || [infoArray count] == 0 ) return; objectEnumerator = [infoArray objectEnumerator]; tempArray = [NSMutableArray array]; while ( baseReference = [objectEnumerator nextObject] ) { if ( baseReference == nil || [baseReference length] == 0 ) return; tempBase = [BCSequenceDNABase performSelector: NSSelectorFromString( baseReference )]; if ( tempBase != nil ) [tempArray addObject: tempBase]; else return; } represents = [tempArray copy]; infoArray = [baseInfo objectForKey: @"Represented by"]; if ( infoArray == nil || [infoArray count] == 0 ) return; objectEnumerator = [infoArray objectEnumerator]; tempArray = [NSMutableArray array]; while ( baseReference = [objectEnumerator nextObject] ) { if ( baseReference == nil || [baseReference length] == 0 ) return; tempBase = [BCSequenceDNABase performSelector: NSSelectorFromString( baseReference )]; if ( tempBase != nil ) [tempArray addObject: tempBase]; else return; } representedBy = [tempArray copy]; } - (void) dealloc { [baseInfo release]; [name release]; [symbolString release]; [complements release]; [represents release]; [representedBy release]; } #pragma mark âBASE INFORMATION METHODS - (NSString *) name { return [[name copy] autorelease]; } - (unichar) symbol { return symbol; } - (NSString *)symbolString { return [[symbolString copy] autorelease]; } - (BOOL) isSingleBase { return isSingleBase; } - (NSString *) savableRepresentation { return [self symbolString]; } - (NSString *) description { return [self symbolString]; } #pragma mark âBASE RELATIONSHIP METHODS /////////////////////////////////////////////////////////// // COMPLEMENTATION METHODS /////////////////////////////////////////////////////////// - (BCSequenceDNABase *)complement { if ( complement == nil ) [self initializeBaseRelationships];; return complement; } - (NSArray *)complements { if ( complements == nil ) [self initializeBaseRelationships];; return [[complements copy] autorelease]; } - (BOOL) complementsBase: (BCSequenceDNABase *)entry { if ( complements == nil ) [self initializeBaseRelationships]; return [complements containsObject: entry]; } - (BOOL) isComplementOfBase: (BCSequenceDNABase *)entry { return [entry complementsBase: self]; } /////////////////////////////////////////////////////////// // REPRESENTATION METHODS FOR AMBIGUOUS BASES /////////////////////////////////////////////////////////// - (NSArray *) representedBases { if ( represents == nil ) [self initializeBaseRelationships]; return [[represents copy] autorelease]; } - (NSArray *) representingBases { if ( representedBy == nil ) [self initializeBaseRelationships]; return [[representedBy copy] autorelease]; } - (BOOL) representsBase: (BCSequenceDNABase *) entry { if ( represents == nil ) [self initializeBaseRelationships]; return [represents containsObject: entry]; } - (BOOL) isRepresentedByBase: (BCSequenceDNABase *) entry { if ( representedBy == nil ) [self initializeBaseRelationships]; return [representedBy containsObject: entry]; } - (NSCharacterSet *) symbolsOfRepresentedBases { if ( representedBy == nil ) [self initializeBaseRelationships]; NSMutableCharacterSet *returnSet = [[[NSMutableCharacterSet alloc] init] autorelease]; NSEnumerator *arrayEnumerator = [representedBy objectEnumerator]; BCSequenceDNABase *aBase; while ( aBase = [arrayEnumerator nextObject] ) { [returnSet addCharactersInString: [aBase symbolString]]; } return [[returnSet copy] autorelease]; } @end