mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-22 19:01:15 +00:00
1121 lines
33 KiB
Objective-C
1121 lines
33 KiB
Objective-C
/** <title>GSXibKeyedUnarchiver.m</title>
|
|
|
|
<abstract>
|
|
These are templates for use with OSX XIB files. These classes are the
|
|
templates and other things which are needed for reading XIB files.
|
|
</abstract>
|
|
|
|
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 <FredKiefer@gmx.de>
|
|
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 <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#import <Foundation/NSXMLDocument.h>
|
|
#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];
|
|
_customClasses= [[NSMutableDictionary alloc] init];
|
|
}
|
|
|
|
- (id) initForReadingWithData: (NSData*)data
|
|
{
|
|
#if GNUSTEP_BASE_HAVE_LIBXML
|
|
NSXMLParser *theParser;
|
|
NSData *theData = data;
|
|
|
|
// Initialize...
|
|
[self _initCommon];
|
|
|
|
// Prepare the XIB data for parsing...
|
|
theData = [self _preProcessXib: data];
|
|
if (theData == nil)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
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
|