/* GSXibLoader Xib (Cocoa XML) model loader Copyright (C) 2010 Free Software Foundation, Inc. Written by: Fred Kiefer Created: March 2010 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import #import #import #import #import #import #import #import #import #import #import #import "AppKit/NSApplication.h" #import "AppKit/NSNib.h" #import "AppKit/NSNibLoading.h" #import "GNUstepGUI/GSModelLoaderFactory.h" #import "GNUstepGUI/GSNibLoading.h" @interface NSApplication (NibCompatibility) - (void) _setMainMenu: (NSMenu*)aMenu; @end // Hack: This allows the class name FirstResponder in NSCustomObject and // correctly returns nil as the corresponding object. @interface FirstResponder: NSObject { } @end @implementation FirstResponder + (id) allocWithZone: (NSZone*)zone { return nil; } @end @interface IBClassDescriptionSource: NSObject { NSString *majorKey; NSString *minorKey; } @end @implementation IBClassDescriptionSource - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"majorKey"]) { ASSIGN(majorKey, [coder decodeObjectForKey: @"majorKey"]); } if ([coder containsValueForKey: @"minorKey"]) { ASSIGN(minorKey, [coder decodeObjectForKey: @"minorKey"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(majorKey); DESTROY(minorKey); [super dealloc]; } @end @interface IBPartialClassDescription: NSObject { NSString *className; NSString *superclassName; NSMutableDictionary *actions; NSMutableDictionary *outlets; IBClassDescriptionSource *sourceIdentifier; } @end @implementation IBPartialClassDescription - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"className"]) { ASSIGN(className, [coder decodeObjectForKey: @"className"]); } if ([coder containsValueForKey: @"superclassName"]) { ASSIGN(superclassName, [coder decodeObjectForKey: @"superclassName"]); } if ([coder containsValueForKey: @"actions"]) { ASSIGN(actions, [coder decodeObjectForKey: @"actions"]); } if ([coder containsValueForKey: @"outlets"]) { ASSIGN(outlets, [coder decodeObjectForKey: @"outlets"]); } if ([coder containsValueForKey: @"sourceIdentifier"]) { ASSIGN(sourceIdentifier, [coder decodeObjectForKey: @"sourceIdentifier"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(className); DESTROY(superclassName); DESTROY(actions); DESTROY(outlets); DESTROY(sourceIdentifier); [super dealloc]; } @end @interface IBClassDescriber: NSObject { NSMutableArray *referencedPartialClassDescriptions; } @end @implementation IBClassDescriber - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"referencedPartialClassDescriptions"]) { ASSIGN(referencedPartialClassDescriptions, [coder decodeObjectForKey: @"referencedPartialClassDescriptions"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(referencedPartialClassDescriptions); [super dealloc]; } @end @interface IBConnection: NSObject { NSString *label; id source; id destination; } - (id) nibInstantiate; - (void) establishConnection; @end @implementation IBConnection - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"label"]) { ASSIGN(label, [coder decodeObjectForKey: @"label"]); } if ([coder containsValueForKey: @"source"]) { ASSIGN(source, [coder decodeObjectForKey: @"source"]); } if ([coder containsValueForKey: @"destination"]) { ASSIGN(destination, [coder decodeObjectForKey: @"destination"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) encodeWithCoder: (NSCoder*)aCoder { // FIXME } - (void) dealloc { DESTROY(label); DESTROY(source); DESTROY(destination); [super dealloc]; } - (id) nibInstantiate { if ([source respondsToSelector: @selector(nibInstantiate)]) { ASSIGN(source, [source nibInstantiate]); } if ([destination respondsToSelector: @selector(nibInstantiate)]) { ASSIGN(destination, [destination nibInstantiate]); } return self; } - (void) establishConnection { } @end @interface IBActionConnection: IBConnection { } @end @implementation IBActionConnection - (void) establishConnection { SEL sel = NSSelectorFromString(label); [destination setTarget: source]; [destination setAction: sel]; } @end @interface IBOutletConnection: IBConnection { } @end @implementation IBOutletConnection - (void) establishConnection { NS_DURING { if (source != nil) { NSString *selName; SEL sel; selName = [NSString stringWithFormat: @"set%@%@:", [[label substringToIndex: 1] uppercaseString], [label substringFromIndex: 1]]; sel = NSSelectorFromString(selName); if (sel && [source respondsToSelector: sel]) { [source performSelector: sel withObject: destination]; } else { const char *nam = [label cString]; const char *type; unsigned int size; int offset; /* * Use the GNUstep additional function to set the instance * variable directly. * FIXME - need some way to do this for libFoundation and * Foundation based systems. */ if (GSObjCFindVariable(source, nam, &type, &size, &offset)) { GSObjCSetVariable(source, offset, size, (void*)&destination); } } } } NS_HANDLER { NSLog(@"Error while establishing connection %@: %@",self,[localException reason]); } NS_ENDHANDLER; } @end @interface IBConnectionRecord: NSObject { IBConnection *connection; int connectionID; } @end @implementation IBConnectionRecord - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"connection"]) { ASSIGN(connection, [coder decodeObjectForKey: @"connection"]); } if ([coder containsValueForKey: @"connectionID"]) { connectionID = [coder decodeIntForKey: @"connectionID"]; } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(connection); [super dealloc]; } - (id) nibInstantiate { ASSIGN(connection, [connection nibInstantiate]); return self; } - (void) establishConnection { [connection establishConnection]; } @end @interface IBObjectRecord: NSObject { int objectID; id object; id children; id parent; } @end @implementation IBObjectRecord - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"objectID"]) { objectID = [coder decodeIntForKey: @"objectID"]; } if ([coder containsValueForKey: @"object"]) { ASSIGN(object, [coder decodeObjectForKey: @"object"]); } if ([coder containsValueForKey: @"children"]) { ASSIGN(children, [coder decodeObjectForKey: @"children"]); } if ([coder containsValueForKey: @"parent"]) { ASSIGN(parent, [coder decodeObjectForKey: @"parent"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(object); DESTROY(children); DESTROY(parent); [super dealloc]; } @end @interface IBMutableOrderedSet: NSObject { NSArray *orderedObjects; } @end @implementation IBMutableOrderedSet - (id) initWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"orderedObjects"]) { ASSIGN(orderedObjects, [coder decodeObjectForKey: @"orderedObjects"]); } } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) dealloc { DESTROY(orderedObjects); [super dealloc]; } @end @interface IBObjectContainer: NSObject { NSMutableArray *connectionRecords; IBMutableOrderedSet *objectRecords; NSMutableDictionary *flattenedProperties; NSMutableDictionary *unlocalizedProperties; id activeLocalization; NSMutableDictionary *localizations; id sourceID; int maxID; } - (id) nibInstantiate; @end @implementation IBObjectContainer - (id) initWithCoder:(NSCoder *)coder { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"connectionRecords"]) { ASSIGN(connectionRecords, [coder decodeObjectForKey: @"connectionRecords"]); } if ([coder containsValueForKey: @"objectRecords"]) { ASSIGN(objectRecords, [coder decodeObjectForKey: @"objectRecords"]); } // We could load more data here, but we currently don't need it. } else { [NSException raise: NSInvalidArgumentException format: @"Can't decode %@ with %@.",NSStringFromClass([self class]), NSStringFromClass([coder class])]; } return self; } - (void) encodeWithCoder: (NSCoder*)aCoder { // FIXME } - (void) dealloc { DESTROY(connectionRecords); DESTROY(objectRecords); DESTROY(flattenedProperties); DESTROY(unlocalizedProperties); DESTROY(activeLocalization); DESTROY(localizations); DESTROY(sourceID); [super dealloc]; } - (id) nibInstantiate { NSEnumerator *en; id obj; en = [connectionRecords objectEnumerator]; // iterate over connections, instantiate, and then establish them. while ((obj = [en nextObject]) != nil) { [obj nibInstantiate]; [obj establishConnection]; } return self; } @end @interface GSXibElement: NSObject { NSString *type; NSDictionary *attributes; NSString *value; NSMutableDictionary *elements; NSMutableArray *values; } - (NSString*) type; - (NSString*) value; - (NSDictionary*) elements; - (NSArray*) values; - (void) addElement: (GSXibElement*)element; - (void) setElement: (GSXibElement*)element forKey: (NSString*)key; - (void) setValue: (NSString*)text; - (NSString*) attributeForKey: (NSString*)key; - (GSXibElement*) elementForKey: (NSString*)key; @end @interface GSXibKeyedUnarchiver: NSKeyedUnarchiver { NSMutableDictionary *objects; NSMutableArray *stack; GSXibElement *currentElement; NSMutableDictionary *decoded; } @end @interface GSXibLoader: GSModelLoader { } @end @implementation GSXibLoader + (NSString *)type { return @"xib"; } + (float) priority { return 4.0; } - (void) awakeData: (NSDictionary*)data withContext: (NSDictionary *)context { NSArray *rootObjects; NSEnumerator *en; id obj; IBObjectContainer *objects; NSMutableArray *topLevelObjects = [context objectForKey: NSNibTopLevelObjects]; //id owner = [context objectForKey: NSNibOwner]; objects = [data objectForKey: @"IBDocument.Objects"]; [objects nibInstantiate]; // FIXME: Use the owner as first root object rootObjects = [data objectForKey: @"IBDocument.RootObjects"]; NSDebugLog(@"rootObjects %@", rootObjects); en = [rootObjects objectEnumerator]; while ((obj = [en nextObject]) != nil) { if ([obj respondsToSelector: @selector(nibInstantiate)]) { obj = [obj nibInstantiate]; [topLevelObjects addObject: obj]; RETAIN(obj); } // instantiate all windows and fill in the top level array. if ([obj isKindOfClass: [NSWindow class]]) { // bring visible windows to front... //if ([obj isVisible]) { [(NSWindow *)obj orderFront: self]; } } else if ([obj isKindOfClass: [NSMenu class]]) { // add the menu... [NSApp _setMainMenu: obj]; } // awaken the object. if ([obj respondsToSelector: @selector(awakeFromNib)]) { [obj awakeFromNib]; } } } - (BOOL) loadModelData: (NSData *)data externalNameTable: (NSDictionary *)context withZone: (NSZone *)zone; { BOOL loaded = NO; NSKeyedUnarchiver *unarchiver = nil; NS_DURING { if (data != nil) { unarchiver = [[GSXibKeyedUnarchiver alloc] initForReadingWithData: data]; if (unarchiver != nil) { NSDictionary *root; NSDebugLog(@"Invoking unarchiver"); [unarchiver setObjectZone: zone]; root = [unarchiver decodeObjectForKey: @"root"]; if (root != nil) { [self awakeData: root withContext: context]; loaded = YES; } else { NSLog(@"Data not found when loading xib."); } RELEASE(unarchiver); } else { NSLog(@"Could not instantiate unarchiver."); } } else { NSLog(@"Data passed to Xib loading method is nil."); } } NS_HANDLER { NSLog(@"Exception occured while loading model: %@",[localException reason]); // TEST_RELEASE(unarchiver); } NS_ENDHANDLER if (loaded == NO) { NSLog(@"Failed to load Xib\n"); } return loaded; } - (NSData *) dataForFile: (NSString *)fileName { NSFileManager *mgr = [NSFileManager defaultManager]; BOOL isDir = NO; NSDebugLog(@"Loading Xib `%@'...\n", fileName); if ([mgr fileExistsAtPath: fileName isDirectory: &isDir]) { if (isDir == NO) { return [NSData dataWithContentsOfFile: fileName]; } else { NSLog(@"Xib file specified %@, is directory.", fileName); } } else { NSLog(@"Xib file specified %@, could not be found.", fileName); } return nil; } @end @implementation GSXibElement - (GSXibElement*) initWithType: (NSString*)typeName andAttributes: (NSDictionary*)attribs { ASSIGN(type, typeName); ASSIGN(attributes, attribs); elements = [[NSMutableDictionary alloc] init]; values = [[NSMutableArray alloc] init]; return self; } - (void) dealloc { DESTROY(type); DESTROY(attributes); DESTROY(elements); DESTROY(values); DESTROY(value); [super dealloc]; } - (NSString*) type { return type; } - (NSString*) value { return value; } - (NSDictionary*) elements { return elements; } - (NSArray*) values { return values; } - (void) addElement: (GSXibElement*)element { [values addObject: element]; } - (void) setElement: (GSXibElement*)element forKey: (NSString*)key { [elements setObject: element forKey: key]; } - (void) setValue: (NSString*)text { ASSIGN(value, text); } - (NSString*) attributeForKey: (NSString*)key { return [attributes objectForKey: key]; } - (GSXibElement*) elementForKey: (NSString*)key { return [elements objectForKey: key]; } - (NSString*) description { return [NSString stringWithFormat: @"GSXibElement <%@> attrs (%@) elements [%@] %@", type, attributes, elements, value, nil]; } @end @implementation GSXibKeyedUnarchiver - (id) initForReadingWithData: (NSData*)data { NSXMLParser *theParser; objects = [[NSMutableDictionary alloc] init]; stack = [[NSMutableArray alloc] init]; decoded = [[NSMutableDictionary alloc] init]; theParser = [[NSXMLParser alloc] initWithData: data]; [theParser setDelegate: self]; NS_DURING { // Parse the XML data [theParser parse]; } NS_HANDLER { NSLog(@"Exception occured while parsing Xib: %@",[localException reason]); DESTROY(self); } NS_ENDHANDLER DESTROY(theParser); return self; } - (void) dealloc { DESTROY(objects); DESTROY(stack); DESTROY(decoded); [super dealloc]; } - (void) parser: (NSXMLParser *)parser foundCharacters: (NSString *)string { [currentElement setValue: string]; } - (void) parser: (NSXMLParser *)parser didStartElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qualifiedName attributes: (NSDictionary *)attributeDict { GSXibElement *element = [[GSXibElement alloc] initWithType: elementName andAttributes: attributeDict]; NSString *key = [attributeDict objectForKey: @"key"]; NSString *ref = [attributeDict objectForKey: @"id"]; if (key != nil) { [currentElement setElement: element forKey: key]; } else { // For Arrays [currentElement addElement: element]; } if (ref != nil) { [objects setObject: element forKey: ref]; } if (![@"archive" isEqualToString: elementName]) { // only used for the root element // push [stack addObject: currentElement]; } if ([@"data" isEqualToString: elementName]) { // only used for the element below root [currentElement setElement: element forKey: @"root"]; } currentElement = element; } - (void) parser: (NSXMLParser *)parser didEndElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qName { if (![@"archive" isEqualToString: elementName]) { // pop currentElement = [stack lastObject]; [stack removeLastObject]; } } - (id) objectForXib: (GSXibElement*)element { NSString *elementName; NSString *key = [element attributeForKey: @"id"]; if (element == nil) return nil; elementName = [element type]; if ([@"object" isEqualToString: elementName]) { GSXibElement *last; NSString *classname = [element attributeForKey: @"class"]; Class c = [self classForClassName: classname]; id o, r; id delegate = [self delegate]; if (c == nil) { c = [[self class] classForClassName: classname]; if (c == nil) { c = NSClassFromString(classname); if (c == nil) { c = [delegate unarchiver: self cannotDecodeObjectOfClassName: classname originalClasses: nil]; if (c == nil) { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: no class for name '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), classname]; } } } } // push last = currentElement; currentElement = element; // Create instance. o = [c allocWithZone: [self zone]]; if (key != nil) [decoded setObject: o forKey: key]; r = [o initWithCoder: self]; if (r != o) { [delegate unarchiver: self willReplaceObject: o withObject: r]; o = r; if (key != nil) [decoded setObject: o forKey: key]; } r = [o awakeAfterUsingCoder: self]; if (r != o) { [delegate unarchiver: self willReplaceObject: o withObject: r]; o = r; if (key != nil) [decoded setObject: o forKey: key]; } if (delegate != nil) { r = [delegate unarchiver: self didDecodeObject: o]; if (r != o) { [delegate unarchiver: self willReplaceObject: o withObject: r]; o = r; if (key != nil) [decoded setObject: o forKey: key]; } } if (key != nil) { // Retained in decoded RELEASE(o); } else AUTORELEASE(o); // pop currentElement = last; return o; } else if ([@"string" isEqualToString: elementName]) { NSString *type = [element attributeForKey: @"type"]; id new = [element value]; if ([type isEqualToString: @"base64-UTF8"]) { NSData *d = [new dataUsingEncoding: NSASCIIStringEncoding]; d = [GSMimeDocument decodeBase64: d]; new = AUTORELEASE([[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding]); } if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"int" isEqualToString: elementName]) { id new = [NSNumber numberWithInt: [[element value] intValue]]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"double" isEqualToString: elementName]) { id new = [NSNumber numberWithDouble: [[element value] doubleValue]]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"bool" isEqualToString: elementName]) { id new = [NSNumber numberWithBool: [[element value] boolValue]]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"reference" isEqualToString: elementName]) { NSString *ref = [element attributeForKey: @"ref"]; if (ref == nil) { return nil; } else { id new = [decoded objectForKey: ref]; if (new == nil) { // push GSXibElement *last = currentElement; element = [objects objectForKey: ref]; currentElement = element; // pop currentElement = last; } return new; } } else if ([@"nil" isEqualToString: elementName]) { return nil; } else if ([@"characters" isEqualToString: elementName]) { id new = [element value]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"integer" isEqualToString: elementName]) { id new = [NSNumber numberWithInteger: [[element value] integerValue]]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"bytes" isEqualToString: elementName]) { id new = [[element value] dataUsingEncoding: NSASCIIStringEncoding allowLossyConversion: NO]; new = [GSMimeDocument decodeBase64: new]; if (key != nil) [decoded setObject: new forKey: key]; return new; } else if ([@"data" isEqualToString: elementName]) { NSDictionary *elements = [element elements]; NSEnumerator *keyEnumerator = [elements keyEnumerator]; NSString *nextKey; GSXibElement *nextXib; id nextObject; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity: [elements count]]; while ((nextKey = (NSString*)[keyEnumerator nextObject]) != nil) { nextXib = (GSXibElement*)[elements objectForKey: nextKey]; nextObject = [self objectForXib: nextXib]; if (nextObject != nil) { [dict setObject: nextObject forKey: nextKey]; } else { NSLog(@"nil object in data dictionary for key %@", nextKey); } } return dict; } else { NSLog(@"Unknown element type %@", elementName); } return nil; } - (id) _decodeArrayOfObjectsForKey: (NSString*)aKey { NSArray *values = [currentElement values]; int max = [values count]; id list[max]; int i; for (i = 0; i < max; i++) { list[i] = [self objectForXib: [values objectAtIndex: i]]; if (list[i] == nil) NSLog(@"No object for %@ at index %d", [values objectAtIndex: i], i); } return [NSArray arrayWithObjects: list count: max]; } - (BOOL) containsValueForKey: (NSString*)aKey { GSXibElement *element = [currentElement elementForKey: aKey]; return (element != nil); } - (id) decodeObjectForKey: (NSString*)aKey { GSXibElement *element = [currentElement elementForKey: aKey]; if (element == nil) return nil; return [self objectForXib: element]; } - (BOOL) decodeBoolForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { return [o boolValue]; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return NO; } - (const uint8_t*) decodeBytesForKey: (NSString*)aKey returnedLength: (NSUInteger*)length { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSData class]] == YES) { *length = [o length]; return [o bytes]; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } *length = 0; return 0; } - (double) decodeDoubleForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { return [o doubleValue]; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return 0.0; } - (float) decodeFloatForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { return [o floatValue]; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return 0.0; } - (int) decodeIntForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { long long l = [o longLongValue]; return l; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return 0; } - (int32_t) decodeInt32ForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { long long l = [o longLongValue]; return l; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return 0; } - (int64_t) decodeInt64ForKey: (NSString*)aKey { id o = [self decodeObjectForKey: aKey]; if (o != nil) { if ([o isKindOfClass: [NSNumber class]] == YES) { long long l = [o longLongValue]; return l; } else { [NSException raise: NSInvalidUnarchiveOperationException format: @"[%@ +%@]: value for key(%@) is '%@'", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey, o]; } } return 0; } @end