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;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.