/**
GSXibKeyedUnarchiver.m
These are templates for use with OSX XIB files. These classes are the
templates and other things which are needed for reading XIB files.
Copyright (C) 2010, 2012, 2017 Free Software Foundation, Inc.
File created by Marcian Lytwyn on 12/30/16 from original code by:
Author: Fred Kiefer
Date: March 2010
Author: Gregory John Casamento
Date: 2012
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#import
#import "GNUstepGUI/GSXibKeyedUnarchiver.h"
#import "GNUstepGUI/GSXibElement.h"
#import "GNUstepGUI/GSNibLoading.h"
#import "GSXib5KeyedUnarchiver.h"
@implementation GSXibKeyedUnarchiver
+ (BOOL) checkXib5: (NSData *)data
{
#if GNUSTEP_BASE_HAVE_LIBXML
// Ensure we have a XIB 5 version...first see if we can parse the XML...
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData: data
options: 0
error: NULL];
if (document == nil)
{
return NO;
}
else
{
// Test to see if this is an Xcode 5 XIB...
NSArray *documentNodes = [document nodesForXPath: @"/document" error: NULL];
// Need at LEAST ONE document node...we should find something a bit more
// specific to check here...
return [documentNodes count] != 0;
}
#else
// We now default to checking XIB 5 versions
return YES;
#endif
}
+ (NSKeyedUnarchiver *) unarchiverForReadingWithData: (NSData *)data
{
NSKeyedUnarchiver *unarchiver = nil;
if ([self checkXib5: data])
{
unarchiver = [[GSXib5KeyedUnarchiver alloc] initForReadingWithData: data];
}
else
{
unarchiver = [[GSXibKeyedUnarchiver alloc] initForReadingWithData: data];
}
return AUTORELEASE(unarchiver);
}
- (NSString *) _substituteClassForClassName: (NSString *)className
{
NSString *result = className;
NSDictionary *dict = [_customClasses objectForKey: className];
if (dict != nil)
{
result = [dict objectForKey: @"parentClassName"];
}
return result;
}
- (NSData *) _preProcessXib: (NSData *)data
{
NSData *result = data;
#if GNUSTEP_BASE_HAVE_LIBXML
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data
options:0
error:NULL];
if (document == nil)
{
NSLog(@"%s:DOCUMENT IS NIL: %@\n", __PRETTY_FUNCTION__, document);
}
else
{
NSArray *customClassNodes = [document nodesForXPath:@"//dictionary[@key=\"flattenedProperties\"]/"
@"string[contains(@key,\"CustomClassName\")]"
error:NULL];
NSMutableDictionary *customClassDict = [NSMutableDictionary dictionary];
if (customClassNodes)
{
NSDebugLLog(@"PREXIB", @"%s:customClassNodes: %@\n", __PRETTY_FUNCTION__, customClassNodes);
// Replace the NSXMLNodes with a dictionary...
NSInteger index = 0;
for (index = 0; index < [customClassNodes count]; ++index)
{
id node = [customClassNodes objectAtIndex:index];
if ([node isMemberOfClass:[NSXMLElement class]])
{
NSString *key = [[node attributeForName:@"key"] stringValue];
if ([key rangeOfString:@"CustomClassName"].location != NSNotFound)
{
[customClassDict setObject:[node stringValue] forKey:key];
}
}
}
}
else
{
NSArray *flatProps = [document nodesForXPath:@"//object[@key=\"flattenedProperties\"]" error:NULL];
if ([flatProps count] == 1)
{
NSInteger index = 0;
NSArray *xmlKeys = [[flatProps objectAtIndex:0] nodesForXPath:
@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.sortedKeys\"]/*" error:NULL];
NSArray *xmlObjs = [[flatProps objectAtIndex:0] nodesForXPath:
@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.values\"]/*" error:NULL];
if ([xmlKeys count] != [xmlObjs count])
{
NSLog(@"%s:keys to objs count mismatch - keys: %d objs: %d\n", __PRETTY_FUNCTION__,
(int)[xmlKeys count], (int)[xmlObjs count]);
}
else
{
for (index = 0; index < [xmlKeys count]; ++index)
{
id key = [[xmlKeys objectAtIndex:index] stringValue];
if ([key rangeOfString:@"CustomClassName"].location != NSNotFound)
{
NSString *cn = [[xmlObjs objectAtIndex: index] stringValue]; // className
[customClassDict setObject:cn forKey:key];
//
// If we are in IB/Gorm build the custom classes map so that we don't instantiate
// classes which don't exist (yet) in IB/Gorm. This allows editing of the model
// in IB/Gorm. If we are in the live app, don't bother as it's a waste of memory.
//
if ([NSClassSwapper isInInterfaceBuilder] == YES)
{
NSUInteger idx = [key rangeOfString: @"."].location;
if (idx != NSNotFound) // unlikely to be NSNotFound...
{
NSString *xpath = [NSString stringWithFormat: @"//object[@class=\"IBClassDescriber\"]"
@"//string[@key=\"className\"][text()=\"%@\"]"
@"/../string[@key=\"superclassName\"]", cn];
NSArray *descriptionObjs = [document nodesForXPath: xpath error: NULL];
if ([descriptionObjs count] > 0)
{
NSString *num = [key substringToIndex: idx];
NSXMLNode *descriptionNode = [descriptionObjs objectAtIndex: 0];
NSString *sc = [descriptionNode stringValue]; // superclassName
NSString *refXPath = [NSString stringWithFormat:
@"//object[@class=\"IBMutableOrderedSet\"][@key=\"objectRecords\"]"
@"/object/object[@class=\"IBObjectRecord\"]/int[@key=\"objectID\"]"
@"[text()=\"%@\"]/../reference[@key=\"object\"]/@ref", num];
NSArray *refNodes = [document nodesForXPath: refXPath error: NULL];
if ([refNodes count] > 0)
{
NSXMLElement *refNode = [refNodes objectAtIndex: 0];
NSString *refId = [refNode stringValue];
[self createCustomClassRecordForId: refId
withParentClass: sc
forCustomClass: cn];
}
}
}
}
}
}
}
}
}
NSDebugLLog(@"PREXIB", @"%s:customClassDict: %@\n", __PRETTY_FUNCTION__, customClassDict);
if ([customClassDict count] > 0)
{
NSArray *objectRecords = nil;
NSEnumerator *en = [[customClassDict allKeys] objectEnumerator];
NSString *key = nil;
while ((key = [en nextObject]) != nil)
{
NSString *keyValue = [key stringByReplacingOccurrencesOfString:@".CustomClassName" withString:@""];
NSString *className = [customClassDict objectForKey: key];
NSString *objectRecordXpath = nil;
objectRecordXpath = [NSString stringWithFormat: @"//object[@class=\"IBObjectRecord\"]/"
@"int[@key=\"objectID\"][text()=\"%@\"]/../reference",
keyValue];
objectRecords = [document nodesForXPath: objectRecordXpath error: NULL];
if (objectRecords == nil)
{
// If that didn't work then it could be a 4.6+ XIB...
objectRecordXpath = [NSString stringWithFormat: @"//object[@class=\"IBObjectRecord\"]/"
@"string[@key=\"id\"][text()=\"%@\"]/../reference",
keyValue];
objectRecords = [document nodesForXPath: objectRecordXpath error: NULL];
}
NSString *refId = nil;
if ([objectRecords count] > 0)
{
id record = nil;
NSEnumerator *oen = [objectRecords objectEnumerator];
while ((record = [oen nextObject]) != nil)
{
if ([record isMemberOfClass:[NSXMLElement class]])
{
if([[[record attributeForName:@"key"] stringValue] isEqualToString:@"object"])
{
NSArray *classNodes = nil;
id classNode = nil;
NSString *refXpath = nil;
refId = [[record attributeForName:@"ref"] stringValue];
refXpath = [NSString stringWithFormat:@"//object[@id=\"%@\"]",refId];
classNodes = [document nodesForXPath:refXpath
error:NULL];
if([classNodes count] > 0)
{
id classAttr = nil;
Class cls = nil;
// If we are in the interface builder app, do not replace
// the existing classes with their custom subclasses.
NSString *clsName = className;
if ([NSClassSwapper isInInterfaceBuilder] == YES)
{
clsName = [self _substituteClassForClassName: className];
}
cls = NSClassFromString(clsName);
classNode = [classNodes objectAtIndex:0];
classAttr = [classNode attributeForName:@"class"];
[classAttr setStringValue: className];
if (cls != nil)
{
if ([cls respondsToSelector:@selector(cellClass)])
{
NSArray *cellNodes = nil;
id cellNode = nil;
id cellClass = [cls cellClass];
NSString *cellXpath = [NSString stringWithFormat:
@"//object[@id=\"%@\"]/object[@key=\"NSCell\"]",refId];
cellNodes = [document nodesForXPath:cellXpath
error:NULL];
if ([cellNodes count] > 0)
{
NSString *cellClassString = NSStringFromClass(cellClass);
id cellAttr = nil;
cellNode = [cellNodes objectAtIndex:0];
cellAttr = [cellNode attributeForName:@"class"];
[cellAttr setStringValue: cellClassString];
}
}
}
}
}
}
}
}
}
}
result = [document XMLData];
RELEASE(document);
}
#endif
return result;
}
- (void) _initCommon
{
objects = [[NSMutableDictionary alloc] init];
stack = [[NSMutableArray alloc] init];
decoded = [[NSMutableDictionary alloc] init];
}
- (id) initForReadingWithData: (NSData*)data
{
#if GNUSTEP_BASE_HAVE_LIBXML
NSXMLParser *theParser;
NSData *theData = data;
// Dictionary which contains custom class information for Gorm/IB.
_customClasses = [[NSMutableDictionary alloc] init];
theData = [self _preProcessXib: data];
if (theData == nil)
{
return nil;
}
// Initialize...
[self _initCommon];
theParser = [[NSXMLParser alloc] initWithData: theData];
[theParser setDelegate: self];
NS_DURING
{
// Parse the XML data
[theParser parse];
}
NS_HANDLER
{
NSLog(@"Exception occurred while parsing Xib: %@",[localException reason]);
DESTROY(self);
}
NS_ENDHANDLER
DESTROY(theParser);
#endif
return self;
}
- (void) dealloc
{
DESTROY(objects);
DESTROY(stack);
DESTROY(decoded);
DESTROY(_customClasses);
[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"];
// FIXME: We should use proper memory management here
AUTORELEASE(element);
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] &&
![@"data" isEqualToString: elementName])
{
// only used for the root element
// push
[stack addObject: currentElement];
}
if (![@"archive" isEqualToString: elementName])
{
currentElement = element;
}
}
- (void) parser: (NSXMLParser*)parser
didEndElement: (NSString*)elementName
namespaceURI: (NSString*)namespaceURI
qualifiedName: (NSString*)qName
{
if (![@"archive" isEqualToString: elementName] &&
![@"data" isEqualToString: elementName])
{
// pop
currentElement = [stack lastObject];
[stack removeLastObject];
}
}
- (id) allocObjectForClassName: (NSString*)clsname
{
Class c = nil;
id delegate = [self delegate];
NSString *classname = clsname;
if ([NSClassSwapper isInInterfaceBuilder] == YES)
{
classname = [self _substituteClassForClassName: classname];
}
c = [self classForClassName: classname];
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];
}
}
}
}
// Create instance.
return [c allocWithZone: [self zone]];
}
- (BOOL) replaceObject: (id)oldObj withObject: (id)newObj
{
NSEnumerator *keyEnumerator = [decoded keyEnumerator];
id key;
BOOL found = NO;
while ((key = [keyEnumerator nextObject]) != nil)
{
id obj = [decoded objectForKey: key];
if (obj == oldObj)
{
found = YES;
break;
}
}
if (found)
{
[decoded setObject: newObj forKey: key];
}
return found;
}
- (id) decodeObjectForXib: (GSXibElement*)element
forClassName: (NSString*)classname
withID: (NSString*)objID
{
GSXibElement *last;
id o, r;
id delegate = [self delegate];
// Create instance.
o = [self allocObjectForClassName: classname];
// Make sure the object stays around, even when replaced.
RETAIN(o);
if (objID != nil)
[decoded setObject: o forKey: objID];
// push
last = currentElement;
currentElement = element;
r = [o initWithCoder: self];
// pop
currentElement = last;
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
r = [o awakeAfterUsingCoder: self];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
if (delegate != nil)
{
r = [delegate unarchiver: self didDecodeObject: o];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
}
// Balance the retain above
RELEASE(o);
if (objID != nil)
{
NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID);
}
return AUTORELEASE(o);
}
/*
This method is a copy of decodeObjectForXib:forClassName:withKey:
The only difference being in the way we decode the object and the
missing context switch.
*/
- (id) decodeDictionaryForXib: (GSXibElement*)element
forClassName: (NSString*)classname
withID: (NSString*)objID
{
id o, r;
id delegate = [self delegate];
// Create instance.
o = [self allocObjectForClassName: classname];
// Make sure the object stays around, even when replaced.
RETAIN(o);
if (objID != nil)
[decoded setObject: o forKey: objID];
r = [o initWithDictionary: [self _decodeDictionaryOfObjectsForElement: element]];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
r = [o awakeAfterUsingCoder: self];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
if (delegate != nil)
{
r = [delegate unarchiver: self didDecodeObject: o];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
}
// Balance the retain above
RELEASE(o);
if (objID != nil)
{
NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID);
}
return AUTORELEASE(o);
}
- (id) objectForXib: (GSXibElement*)element
{
NSString *elementName;
NSString *objID;
if (element == nil)
return nil;
NSDebugLLog(@"XIB", @"decoding element %@", element);
objID = [element attributeForKey: @"id"];
if (objID)
{
id new = [decoded objectForKey: objID];
if (new != nil)
{
// The object was already decoded as a reference
return new;
}
}
elementName = [element type];
if ([@"object" isEqualToString: elementName])
{
NSString *classname = [element attributeForKey: @"class"];
return [self decodeObjectForXib: element
forClassName: classname
withID: objID];
}
else if ([@"string" isEqualToString: elementName])
{
id new = [element value];
// Handle newer format as well
if ([[element attributeForKey: @"type"] isEqualToString: @"base64-UTF8"] ||
[[element attributeForKey: @"base64-UTF8"] boolValue])
{
NSData *d = [[NSData alloc] initWithBase64EncodedString: new
options: NSDataBase64DecodingIgnoreUnknownCharacters];
new = AUTORELEASE([[NSString alloc] initWithData: d
encoding: NSUTF8StringEncoding]);
RELEASE(d);
}
// empty strings are not nil!
if (new == nil)
new = @"";
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"int" isEqualToString: elementName])
{
id new = [NSNumber numberWithInt: [[element value] intValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"double" isEqualToString: elementName])
{
id new = [NSNumber numberWithDouble: [[element value] doubleValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"bool" isEqualToString: elementName])
{
id new = [NSNumber numberWithBool: [[element value] boolValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"integer" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithInteger: [value integerValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"real" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithFloat: [value floatValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"boolean" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithBool: [value boolValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"point" isEqualToString: elementName])
{
NSPoint point = [self decodePointForKey: [element attributeForKey: @"key"]];
id new = [NSValue valueWithPoint: point];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"size" isEqualToString: elementName])
{
NSSize size = [self decodeSizeForKey: [element attributeForKey: @"key"]];
id new = [NSValue valueWithSize: size];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"rect" isEqualToString: elementName])
{
NSRect rect = [self decodeRectForKey: [element attributeForKey: @"key"]];
id new = [NSValue valueWithRect: rect];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"reference" isEqualToString: elementName])
{
NSString *ref = [element attributeForKey: @"ref"];
if (ref == nil)
{
return nil;
}
else
{
id new = [decoded objectForKey: ref];
// FIXME: We need a marker for nil
if (new == nil)
{
//NSLog(@"Decoding reference %@", ref);
element = [objects objectForKey: ref];
if (element != nil)
{
// Decode the real object
new = [self objectForXib: element];
}
}
return new;
}
}
else if ([@"nil" isEqualToString: elementName])
{
return nil;
}
else if ([@"characters" isEqualToString: elementName])
{
id new = [element value];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"bytes" isEqualToString: elementName])
{
id new = AUTORELEASE([[NSData alloc] initWithBase64EncodedString: [element value]
options: NSDataBase64DecodingIgnoreUnknownCharacters]);
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"array" isEqualToString: elementName])
{
NSString *classname = [element attributeForKey: @"class"];
if (classname == nil)
{
classname = @"NSArray";
}
return [self decodeObjectForXib: element
forClassName: classname
withID: objID];
}
else if ([@"dictionary" isEqualToString: elementName])
{
NSString *classname = [element attributeForKey: @"class"];
if (classname == nil)
{
classname = @"NSDictionary";
}
return [self decodeDictionaryForXib: element
forClassName: classname
withID: objID];
}
else
{
//NSLog(@"Unknown element type %@", elementName);
}
return nil;
}
- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey
{
// FIXME: This is wrong but the only way to keep the code for
// [NSArray-initWithCoder:] working
return [self _decodeArrayOfObjectsForElement: currentElement];
}
- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element
{
NSArray *values = [element 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];
}
- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element
{
NSDictionary *elements = [element elements];
NSEnumerator *en;
NSString *key;
NSMutableDictionary *dict;
dict = [[NSMutableDictionary alloc] init];
en = [elements keyEnumerator];
while ((key = [en nextObject]) != nil)
{
id obj = [self objectForXib: [elements objectForKey: key]];
if (obj == nil)
NSLog(@"No object for %@ at key %@", [elements objectForKey: key], key);
else
[dict setObject: obj forKey: key];
}
return AUTORELEASE(dict);
}
/*
Extension method to decode the object id of an object referenced by its key.
*/
- (NSString *) decodeReferenceForKey: (NSString*)aKey
{
GSXibElement *element = [currentElement elementForKey: aKey];
NSString *objID;
if (element == nil)
return nil;
objID = [element attributeForKey: @"id"];
if (objID)
{
return objID;
}
objID = [element attributeForKey: @"ref"];
if (objID)
{
return objID;
}
return nil;
}
- (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) ||
([o isKindOfClass: [NSString 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) ||
([o isKindOfClass: [NSString 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) ||
([o isKindOfClass: [NSString 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) ||
([o isKindOfClass: [NSString 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) ||
([o isKindOfClass: [NSString 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) ||
([o isKindOfClass: [NSString 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;
}
- (NSDictionary *) decoded
{
return decoded;
}
- (NSDictionary *) customClasses
{
return _customClasses;
}
- (void) createCustomClassRecordForId: (NSString *)theId
withParentClass: (NSString *)parentClassName
forCustomClass: (NSString *)customClassName
{
if (theId == nil || customClassName == nil)
return;
NSMutableDictionary *dict =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
parentClassName, @"parentClassName",
theId, @"id",nil];
[_customClasses setObject: dict
forKey: customClassName];
}
@end