This is ComicsObj.m in view mode; [Download] [Up]
/* File: ComicsObj.m Contains: Source code for the 3 classes used for the Comics database management: CIssue, CTitle, CComics Written by: Eric Simenel Created: May 1997 Copyright: (c)1997 by Apple Computer, Inc., all rights reserved. Change History (most recent first): You may incorporate this sample code into your applications without restriction, though the sample code has been provided "AS IS" and the responsibility for its operation is 100% yours. However, what you are not permitted to do is to redistribute the source as "DSC Sample Code" after having made changes. If you're going to re-distribute the source, we require that you make it clear in the source that the code was descended from Apple Sample Code, but that you've made changes. */ #import "ComicsObj.h" // ComicsObj.h is a project header, so that comicsBase is known in all other source files // There is only one instantiated object (when the main nib opens) of class CComics, and // comicsBase is it. Simpler to use than having a "comics" outlet and a "comics" accessor // in the main controller "VerifyController", and having all other source files refer to // the database as [[NSApp delegate] comics] CComics *comicsBase = nil; // The following global arrays are here to "speed up" number to string conversion, // see the initGlobals method of CComics tnumstr gnumstr; Str3 gnums[1000]; // Since we're dealing with American Comics only, the strings for edit and buy month are // "JAN", "FEB", etc. contained in this array. Str3 gmonths[12]; // This is the notification which will be sent by the InputController when the content of // the database is changed. Some other Controllers will register to be notified. NSString *ComicsDidChangeNotification = @"ComicsDidChangeNotification"; @implementation CIssue - (id)init { return [self initWithIsh:-1 withEdit:-1 withBuy:-1 withFlag:-1]; } - (id)initWithIsh:(short)ish withEdit:(short)edit withBuy:(short)buy withFlag:(short)flag { if (self = [super init]) { [self setIssueNumber:ish]; [self setEditMonth:edit]; [self setBuyMonth:buy]; [self setIssueFlags:flag]; } return self; } - (void)setIssueFlags:(short)value { issueFlags = value; } - (short)issueFlags { return issueFlags; } - (void)setIssueNumber:(short)value { issueNumber = value; } - (short)issueNumber { return issueNumber; } - (void)setEditMonth:(short)value { editMonth = value; } - (short)editMonth { return editMonth; } - (void)setBuyMonth:(short)value { buyMonth = value; } - (short)buyMonth { return buyMonth; } - (NSString *)grade { if (issueFlags & mskMint) return @"Mint"; if (issueFlags & mskFine) return @"Fine"; if (issueFlags & mskMiss) return @"Missing"; if (issueFlags & mskPoor) return @"Poor"; else return @"Good"; } - (NSString *)ishtype { if (issueFlags & mskComics ) return @"Comics"; if (issueFlags & mskNewForm ) return @"New Format"; if (issueFlags & mskLuxe ) return @"Luxe"; else return @"Magazine"; } - (NSString *)content { if (issueFlags & mskStory ) return @"Story"; if (issueFlags & mskInfo ) return @"Information"; else return @"Reprint"; } - (id)initWithCoder:(NSCoder *)coder { short temp; [coder decodeValuesOfObjCTypes:"s", &temp]; [self setIssueNumber:temp]; [coder decodeValuesOfObjCTypes:"s", &temp]; [self setEditMonth:temp]; [coder decodeValuesOfObjCTypes:"s", &temp]; [self setBuyMonth:temp]; [coder decodeValuesOfObjCTypes:"s", &temp]; [self setIssueFlags:temp]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { short temp; temp = [self issueNumber]; [coder encodeValuesOfObjCTypes:"s", &temp]; temp = [self editMonth]; [coder encodeValuesOfObjCTypes:"s", &temp]; temp = [self buyMonth]; [coder encodeValuesOfObjCTypes:"s", &temp]; temp = [self issueFlags]; [coder encodeValuesOfObjCTypes:"s", &temp]; } @end @implementation CTitle - (id)init { return [self initWithAbb:@"ZZZ" withTitle:@"Zzzzzzzzzz" withFlag:-1]; } - (id)initWithAbb:(NSString *)theAbb withTitle:(NSString *)theTitle withFlag:(short)flag { if (self = [super init]) { [self setAbb:theAbb]; [self setTitle:theTitle]; [self setTitleFlags:flag]; [self setIssues:[NSMutableArray array]]; } return self; } - (void)dealloc { [abb release]; [title release]; [issues release]; [super dealloc]; } - (void)setTitleFlags:(short)value { titleFlags = value; } - (short)titleFlags { return titleFlags; } - (void)setAbb:(NSString *)value { [abb autorelease]; abb = [value copy]; } - (NSString *)abb { return abb; } - (void)setTitle:(NSString *)value { [title autorelease]; title = [value copy]; } - (NSString *)title { return title; } - (NSString *)brand { return (titleFlags & mskMarvel)?@"Marvel":((titleFlags & mskDC)?@"DC":@"Other"); } - (NSString *)tstate { return (titleFlags & mskLive)?@"Live":@"Dead"; } - (NSString *)series { return (titleFlags & mskLong)?@"Long":@"Mini"; } - (NSString *)kind { return (titleFlags & mskMain)?@"Main":@"Dual"; } - (short)nbIssues { return [issues count]; } - (void)setIssues:(NSMutableArray *)theArray { [theArray retain]; [issues release]; issues = theArray; } - (NSMutableArray *)issues { return issues; } // Returns a NSString containing the list of the issues of this title with the following format: // "1-66,94-345" meaning all issues between 1 and 66 (included) and all issues between 94 and 345. // Optimized (other time) to the point where it's obfuscated... Sorry. - (NSString *)listIssues { NSString *result; long ref, oref, nref, diff, i = 0, n = [issues count]; char str[1000], *starts, *s; starts = s = str; while (i < n) { nref = oref = ref = [[issues objectAtIndex:i] issueNumber]; *((long *)s) = gnumstr.nstrs[ref]; s += gnumstr.lens[ref]; while ((++i < n) && ((ref+1) == (nref = [[issues objectAtIndex:i] issueNumber]))) ref = nref; if ((diff = (ref - oref)) > 0) { *s++ = (diff > 1)?'-':','; *((long *)s) = gnumstr.nstrs[ref]; s += gnumstr.lens[ref]; } *s++ = ','; } if (s != starts) *--s = 0; else *s = 0; result = [[NSString alloc] initWithCString:str]; [result autorelease]; return result; } - (id)initWithCoder:(NSCoder *)coder { short temp; [self setAbb: [coder decodeObject]]; [self setTitle: [coder decodeObject]]; [self setIssues: [coder decodeObject]]; [coder decodeValuesOfObjCTypes:"s", &temp]; [self setTitleFlags:temp]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { short temp = [self titleFlags]; [coder encodeObject: [self abb]]; [coder encodeObject: [self title]]; [coder encodeObject: [self issues]]; [coder encodeValuesOfObjCTypes:"s", &temp]; } - (short)findIssue:(short)byIshNum { int i, j = -1, found = 0, n = [issues count]; for(i = 0; (i < n) && (!found); i++) if (found = ([[issues objectAtIndex:i] issueNumber] == byIshNum)) j = i; return j; } - (short)addIssue:(CIssue *)theIssue { int i, j = -1, ok = 1, n = [issues count]; CIssue *thisIssue; for(i = 0; (i < n) && ok; i++) { ok = ([(thisIssue = [issues objectAtIndex:i]) issueNumber] != [theIssue issueNumber]); if (j == -1) if ([theIssue editMonth] < [thisIssue editMonth]) j = i; } if (ok) { [comicsBase setIssue:theIssue]; [comicsBase modNbIssues:1]; if (j == -1) [issues addObject:theIssue]; else [issues insertObject:theIssue atIndex:j]; return 0; } else return -1; } - (short)modIssue:(CIssue *)oldIssue withNewIssue:(CIssue *)newIssue { short theIndex; if ([oldIssue issueNumber] != [newIssue issueNumber]) return -2; if ((theIndex = [self findIssue:[oldIssue issueNumber]]) == -1) return -1; [comicsBase setIssue:newIssue]; [issues replaceObjectAtIndex:theIndex withObject:newIssue]; #if debug NSLog(@"CTitle::modIssue"); #endif return 0; } - (short)deleteIssue:(CIssue *)theIssue { short theIndex; if ((theIndex = [self findIssue:[theIssue issueNumber]]) == -1) return -1; [comicsBase modNbIssues:-1]; [issues removeObjectAtIndex:theIndex]; return 0; } // Comparison methods used in sortArray of CComics - (NSComparisonResult)compareAbb:(CTitle *)aTitle { return [abb compare:[aTitle abb]]; } - (NSComparisonResult)compareTitle:(CTitle *)aTitle { return [title compare:[aTitle title]]; } - (NSComparisonResult)compareChrono:(CTitle *)aTitle { short firstEdit = [[issues objectAtIndex:0] editMonth]; short theOtherFirstEdit = [[[aTitle issues] objectAtIndex:0] editMonth]; if (firstEdit == theOtherFirstEdit) return NSOrderedSame; else if (firstEdit < theOtherFirstEdit) return NSOrderedAscending; else return NSOrderedDescending; } - (NSComparisonResult)compareMaxIssue:(CTitle *)aTitle { short lastMax = [[issues lastObject] issueNumber]; short theOtherLastMax = [[[aTitle issues] lastObject] issueNumber]; if (lastMax == theOtherLastMax) return NSOrderedSame; else if (lastMax < theOtherLastMax) return NSOrderedDescending; else return NSOrderedAscending; } @end char strdate[10]; @implementation CComics + (char *)cStrDate:(short)theMonth { strcpy(strdate, gmonths[(theMonth-1)%12]); strcat(strdate, gnums[(theMonth-1)/12]); return strdate; } + (NSString *)nsStrDate:(short)theMonth { return [NSString stringWithCString:[CComics cStrDate:theMonth]]; } // Private helper to get database path - (NSString *)dbPath { NSMutableString *result = [[NSMutableString alloc] initWithString:[[NSBundle mainBundle] bundlePath]]; [result autorelease]; [result appendString:@"/ComicsDataBase"]; #if debug NSLog(@"dbPath = '%@'", result); #endif return result; } // Fills the global arrays to speed up number to string conversion - (void)initGlobals { long i, j, n, *pi, *pl; char ns[] = "0123456789"; char s[5], *p1, *p2; *(pi = &gnumstr.lens[0]) = 1; pl = &gnumstr.nstrs[0]; *(p1 = (char *)pl) = '0'; for(i=1; i<1001; i++) { j = i; p1 = s; n = 0; while (j>0) { *p1++ = ns[j % 10]; j = j / 10; n++; } *++pi = n; p2 = (char *)(++pl); while (n--) *p2++ = *--p1; } strcpy(gmonths[ 0], "JAN"); strcpy(gmonths[ 1], "FEB"); strcpy(gmonths[ 2], "MAR"); strcpy(gmonths[ 3], "APR"); strcpy(gmonths[ 4], "MAY"); strcpy(gmonths[ 5], "JUN"); strcpy(gmonths[ 6], "JUL"); strcpy(gmonths[ 7], "AUG"); strcpy(gmonths[ 8], "SEP"); strcpy(gmonths[ 9], "OCT"); strcpy(gmonths[10], "NOV"); strcpy(gmonths[11], "DEC"); for(i=0; i<=9; i++) {gnums[i][0] = 32; gnums[i][1] = 32; gnums[i][2] = ns[i]; gnums[i][3] = 0;} for(i=1; i<=9; i++) for(j=0; j<= 9; j++) {gnums[i*10+j][0] = 32; gnums[i*10+j][1] = ns[i]; gnums[i*10+j][2] = ns[j]; gnums[i*10+j][3] = 0;} for(i=1; i<=9; i++) for(j=0; j<= 9; j++) for(n=0; n<=9; n++) {gnums[i*100+j*10+n][0] = ns[i]; gnums[i*100+j*10+n][1] = ns[j]; gnums[i*100+j*10+n][2] = ns[n]; gnums[i*100+j*10+n][3] = 0;} } // Private method to convert the Mac database which I already have. // Better than reenter 22,000+ issues... - (void)convertFromMacFormat { CTitle *theTitle; CIssue *theIssue; short i, j, nbt, nbi, flag, ish, edit, buy; unsigned char *buf, sl; long pos, pos2; char thestring[100]; NSString *theAbbStr, *theTitleStr; NSMutableString *fileName = [[NSMutableString alloc] initWithString:[[NSBundle mainBundle] pathForResource:@"Comics.mac" ofType:@""]]; NSFileManager *nsfm = [NSFileManager defaultManager]; NSData *data = [nsfm contentsAtPath:fileName]; buf = (unsigned char *)[data bytes]; #if debug NSLog(@"fileName = '%@'", fileName); #endif nbt = (buf[0] << 8) | buf [1]; #if debug NSLog(@"nbt = %d", nbt); #endif pos = 14 + (4 * nbt); for(i = 0; i < nbt; i++) { sl = buf[pos]; pos2 = pos+1; for(j = 0; j < sl; j++) thestring[j] = buf[pos2++]; thestring[sl] = 0; theAbbStr = [[NSString alloc] initWithCString:thestring]; pos += 6; sl = buf[pos]; pos2 = pos+1; for(j = 0; j < sl; j++) thestring[j] = buf[pos2++]; thestring[sl] = 0; theTitleStr = [[NSString alloc] initWithCString:thestring]; pos += 50; flag = (buf[pos] << 8) | buf [pos+1]; pos += 2; theTitle = [[CTitle alloc] initWithAbb:theAbbStr withTitle:theTitleStr withFlag:flag]; [comicsBase addTitle:theTitle]; [theTitle release]; [theAbbStr release]; [theTitleStr release]; nbi = (buf[pos] << 8) | buf [pos+1]; pos += 2; for(j = 0; j < nbi; j++) { ish = (buf[pos] << 8) | buf [pos+1]; pos += 2; edit = (buf[pos] << 8) | buf [pos+1]; pos += 2; buy = (buf[pos] << 8) | buf [pos+1]; pos += 2; flag = (buf[pos] << 8) | buf [pos+1]; pos += 2; theIssue = [[CIssue alloc] initWithIsh:ish withEdit:edit withBuy:buy withFlag:flag]; [theTitle addIssue:theIssue]; [theIssue release]; } } [fileName release]; } // if realLoad is 1, then get the data from the Yellow archived database using NSUnarchiver // if realLoad is 0, then get the data from the Mac database #define realLoad 0 #define withConvert 1 - (id)init { if (self = [super init]) { [self initGlobals]; [self reset]; #if realLoad self = [NSUnarchiver unarchiveObjectWithFile:[self dbPath]]; [self retain]; comicsBase = self; #else [self setTitles:[NSMutableArray array]]; comicsBase = self; #if withConvert [self convertFromMacFormat]; #endif [self save:nil]; #endif #if debug NSLog(@"startEditMonth = %d", startEditMonth); NSLog(@"lastEditMonth = %d", lastEditMonth); NSLog(@"startBuyMonth = %d", startBuyMonth); NSLog(@"lastBuyMonth = %d", lastBuyMonth); #endif } return self; } - (void)dealloc { [titles release]; [super dealloc]; } - (void)reset { NSCalendarDate *date = [NSCalendarDate calendarDate]; nbIssues = 0; maxIssue = -1; startBuyMonth = [date monthOfYear] + 12 * ([date yearOfCommonEra] -1900); lastBuyMonth = startBuyMonth - 6; startEditMonth = startBuyMonth + 3; lastEditMonth = lastBuyMonth; } - (void)setIssue:(CIssue *)theIssue { if ([theIssue issueNumber] > maxIssue) maxIssue = [theIssue issueNumber]; if ([theIssue editMonth] < startEditMonth) startEditMonth = [theIssue editMonth]; if ([theIssue editMonth] > lastEditMonth) lastEditMonth = [theIssue editMonth]; if (!([theIssue issueFlags] & mskMiss)) if ([theIssue buyMonth] < startBuyMonth) startBuyMonth = [theIssue buyMonth]; if ([theIssue buyMonth] > lastBuyMonth) lastBuyMonth = [theIssue buyMonth]; } - (void)setAll { int i, j, n = [titles count], n2; NSMutableArray* theseIssues; [self reset]; for(i = 0; i < n; i++) { CTitle *thisTitle = [titles objectAtIndex:i]; nbIssues += [thisTitle nbIssues]; theseIssues = [thisTitle issues]; n2 = [theseIssues count]; for(j = 0; j < n2; j++) { CIssue *thisIssue = [theseIssues objectAtIndex:j]; [self setIssue:thisIssue]; } } } - (void)modNbIssues:(short)delta { nbIssues += delta; } - (short)nbIssues { return nbIssues; } - (short)maxIssue { return maxIssue; } - (short)startEditMonth { return startEditMonth; } - (short)lastEditMonth { return lastEditMonth; } - (short)startBuyMonth { return startBuyMonth; } - (short)lastBuyMonth { return lastBuyMonth; } - (short)nbTitles { return [titles count]; } - (NSMutableArray *)titles { return titles; } - (void)setTitles:(NSMutableArray *)theArray { [theArray retain]; [titles release]; titles = theArray; } - (void)save:(id)sender { [NSArchiver archiveRootObject:self toFile:[self dbPath]]; } - (id)initWithCoder:(NSCoder *)coder { [self setTitles: [coder decodeObject]]; [self setAll]; return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject: [self titles]]; } // This method first clears the array which is passed in as a parameter, // then fills it only with the titles that conform to the criteria (also passed in as parameters), // and then sort those title using the comparison methods of CTitle - (void)sortArray:(NSMutableArray *)theArray withBrand:(short)brand withSeries:(short)series withKind:(short)kind withState:(short)state withSort:(short)sort { int i, ok, n = [titles count]; CTitle* thisTitle; short brands[2] = {mskMarvel, mskDC | mskOther}; short seriesa[2] = {mskLong, mskMini}; short states[2] = {mskDead, mskLive}; short kinds[2] = {mskMain, mskDual}; // clear [theArray removeAllObjects]; // fill for(i = 0; i < n; i++) { thisTitle = [titles objectAtIndex:i]; ok = (brand == 0) || ([thisTitle titleFlags] & brands[brand-1]); if (ok) ok = (series == 0) || ([thisTitle titleFlags] & seriesa[series-1]); if (ok) ok = (state == 0) || ([thisTitle titleFlags] & states[state-1]); if (ok) ok = (kind == 0) || ([thisTitle titleFlags] & kinds[kind-1]); if (ok) [theArray addObject:thisTitle]; } // sort switch(sort) { case 0: [theArray sortUsingSelector:@selector(compareAbb:)]; break; case 1: [theArray sortUsingSelector:@selector(compareTitle:)]; break; case 2: [theArray sortUsingSelector:@selector(compareChrono:)]; break; case 3: [theArray sortUsingSelector:@selector(compareMaxIssue:)]; break; } } - (short)findTitleByAbb:(NSString *)theAbb { int i, j = -1, found = 0, n = [titles count]; for(i = 0; (i < n) && (!found); i++) if (found = [[[titles objectAtIndex:i] abb] isEqualToString:theAbb]) j = i; return j; } - (short)findTitleByTitle:(NSString *)theTitle { int i, j = -1, found = 0, n = [titles count]; for(i = 0; (i < n) && (!found); i++) if (found = [[[titles objectAtIndex:i] title] isEqualToString:theTitle]) j = i; return j; } - (short)addTitle:(CTitle *)theTitle { if ([self findTitleByAbb:[theTitle abb]] != -1) return -1; if ([self findTitleByTitle:[theTitle title]] != -1) return -2; [titles addObject:theTitle]; return 0; } - (short)modTitle:(CTitle *)oldTitle withNewTitle:(CTitle *)newTitle { short theIndex = [self findTitleByAbb:[oldTitle abb]]; if (theIndex == -1) return -1; [newTitle setIssues:[oldTitle issues]]; [titles replaceObjectAtIndex:theIndex withObject:newTitle]; return 0; } - (short)deleteTitle:(CTitle *)theTitle { short theIndex = [self findTitleByAbb:[theTitle abb]]; if (theIndex == -1) return -1; [comicsBase modNbIssues:-[theTitle nbIssues]]; [titles removeObjectAtIndex:theIndex]; return 0; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.