#import "ElementLSTB.h" #import "ElementLSTC.h" #import "ElementLSTE.h" #import "ElementOCNT.h" // implements LSTB, LSTZ @implementation ElementLSTB - (id)initForType:(NSString *)t withLabel:(NSString *)l { self = [super initForType:t withLabel:l]; if(!self) return nil; subElements = [[NSMutableArray alloc] init]; groupElementTemplate = self; return self; } - (void)dealloc { [subElements release]; [super dealloc]; } - (id)copyWithZone:(NSZone *)zone { ElementLSTB *element = [super copyWithZone:zone]; if(!element) return nil; ElementOCNT *counter = nil; NSMutableArray *array = [element subElements]; // get empty array created by -initWithType:label: [element setGroupElementTemplate:groupElementTemplate]; // override group template (init sets this to self) unsigned count = [[groupElementTemplate subElements] count]; for(unsigned i = 0; i < count; i++) { Element *subToClone = [[groupElementTemplate subElements] objectAtIndex:i]; if([subToClone class] == [ElementLSTB class] || [subToClone class] == [ElementLSTC class]) { // instead create a terminating 'LSTE' item, using LSTB/C item's label ElementLSTE *end = [ElementLSTE elementForType:@"LSTE" withLabel:[subToClone label]]; [array addObject:end]; [end setParentArray:array]; [end setGroupElementTemplate:[(id)subToClone groupElementTemplate]]; [end setCountElement:counter]; if([[subToClone type] isEqualToString:@"LSTZ"]) [end setWritesZeroByte:YES]; } else { Element *clone = [[subToClone copy] autorelease]; if([clone class] == [ElementOCNT class]) counter = (ElementOCNT *)clone; [array addObject:clone]; } } return element; } - (void)readSubElementsFrom:(TemplateStream *)stream { while([stream bytesToGo] > 0) { Element *element = [stream readOneElement]; if([[element type] isEqualToString:@"LSTE"]) { if([type isEqualToString:@"LSTZ"]) [(ElementLSTE *)element setWritesZeroByte:YES]; break; } [subElements addObject:element]; } } - (void)readDataForElements:(TemplateStream *)stream { int counterValue = 0; ElementOCNT *counter = nil; for(unsigned i = 0; i < [subElements count]; i++) { Element *element = [subElements objectAtIndex:i]; // set up the counter tracking if([element class] == [ElementOCNT class]) counter = element; // decrement the counter if we have list items if([element class] == [ElementLSTC class]) counterValue--; // if we get to the end of the list and need more items, create them if([element class] == [ElementLSTE class] && counterValue > 0) { int index = [subElements indexOfObject:element]; while(counterValue--) { // create subarray for new data ElementLSTB *list = [[[(ElementLSTE *)element groupElementTemplate] copy] autorelease]; [subElements insertObject:list atIndex:index++]; [list setParentArray:subElements]; [list setCountElement:counter]; // read data into it and increment loop [list readDataForElements:stream]; i++; } } // actually read the data for this item [element readDataFrom:stream]; // now that we've read the (possibly counter) data, save it to the local variable if(counter) counterValue = [counter value]; } } - (void)readDataFrom:(TemplateStream *)stream { BOOL isZeroTerminated = [type isEqualToString:@"LSTZ"]; unsigned int bytesToGoAtStart = [stream bytesToGo]; if(isZeroTerminated) { char termByte = 0; [stream peekAmount:1 toBuffer:&termByte]; if(termByte) [self readDataForElements:stream]; } else [self readDataForElements:stream]; /* Read additional elements until we have enough items, except if we're not the first item in our list. */ if(parentArray) { while([stream bytesToGo] > 0) { if(isZeroTerminated) { char termByte = 0; [stream peekAmount:1 toBuffer:&termByte]; if(termByte == 0) break; } // actually read the item Element *nextItem = [[groupElementTemplate copy] autorelease]; [nextItem setParentArray:nil]; // Make sure it doesn't get into this "if" clause. [parentArray addObject:nextItem]; // Add it below ourselves. [nextItem readDataFrom:stream]; // Read it the same way we were. [nextItem setParentArray:parentArray]; // Set parentArray *after* -readDataFrom: so it doesn't pass the if(parentArray) check above. } // now add a terminating 'LSTE' item, using this item's label ElementLSTE *end = [ElementLSTE elementForType:@"LSTE" withLabel:label]; [parentArray addObject:end]; [end setParentArray:parentArray]; [end setGroupElementTemplate:groupElementTemplate]; [end setCountElement:countElement]; if(isZeroTerminated) { [end setWritesZeroByte:YES]; [end readDataFrom:stream]; } // if it's an empty list delete this LSTB so we only have the empty LSTE. if(bytesToGoAtStart == 0) [parentArray removeObject:self]; } } // Before writeDataTo:is called, this is called to calculate the final resource size: // This returns the sizes of all our sub-elements. If you subclass, add to that the size // of this element itself. - (unsigned int)sizeOnDisk { unsigned int size = 0; NSEnumerator *enumerator = [subElements objectEnumerator]; while(Element *element = [enumerator nextObject]) size += [element sizeOnDisk]; return size; } - (void)writeDataTo:(TemplateStream *)stream { // Writes out the data of all our sub-elements here: NSEnumerator *enumerator = [subElements objectEnumerator]; while(Element *element = [enumerator nextObject]) [element writeDataTo:stream]; } #pragma mark - - (void)setSubElements:(NSMutableArray *)a { id old = subElements; subElements = [a retain]; [old release]; } - (NSMutableArray *)subElements { return subElements; } - (int)subElementCount { return [subElements count]; } - (Element *)subElementAtIndex:(int)n { return [subElements objectAtIndex:n]; } - (void)setGroupElementTemplate:(ElementLSTB *)e { // do not retain, -init sets this to 'self' initially! groupElementTemplate = e; } - (ElementLSTB *)groupElementTemplate { return groupElementTemplate; } - (void)setCountElement:(ElementOCNT *)e { // do not retain sibling element countElement = e; } - (ElementOCNT *)countElement { return countElement; } - (IBAction)createListEntry:(id)sender { ElementLSTB *list = [[groupElementTemplate copy] autorelease]; [parentArray insertObject:list atIndex:[parentArray indexOfObject:self]]; [list setParentArray:parentArray]; [list setCountElement:countElement]; [countElement increment]; } - (IBAction)clear:(id)sender { [countElement decrement]; [parentArray removeObject:self]; } - (NSString *)stringValue { return @""; } - (void)setStringValue:(NSString *)str {} - (BOOL)editable { return NO; } @end