home *** CD-ROM | disk | FTP | other *** search
- /*
- 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