mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
471 lines
15 KiB
Plaintext
471 lines
15 KiB
Plaintext
/*
|
|
* CPKeyedUnarchiver.j
|
|
* Foundation
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* 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.1 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; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
import "CPNull.j"
|
|
import "CPCoder.j"
|
|
|
|
|
|
var _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector = 1,
|
|
_CPKeyedUnarchiverDidDecodeObjectSelector = 1 << 1,
|
|
_CPKeyedUnarchiverWillReplaceObjectWithObjectSelector = 1 << 2,
|
|
_CPKeyedUnarchiverWillFinishSelector = 1 << 3,
|
|
_CPKeyedUnarchiverDidFinishSelector = 1 << 4;
|
|
|
|
var _CPKeyedArchiverNullString = "$null"
|
|
|
|
_CPKeyedArchiverUIDKey = "CP$UID",
|
|
|
|
_CPKeyedArchiverTopKey = "$top",
|
|
_CPKeyedArchiverObjectsKey = "$objects",
|
|
_CPKeyedArchiverArchiverKey = "$archiver",
|
|
_CPKeyedArchiverVersionKey = "$version",
|
|
|
|
_CPKeyedArchiverClassNameKey = "$classname",
|
|
_CPKeyedArchiverClassesKey = "$classes",
|
|
_CPKeyedArchiverClassKey = "$class";
|
|
|
|
var _CPKeyedUnarchiverArrayClass = Nil,
|
|
_CPKeyedUnarchiverStringClass = Nil,
|
|
_CPKeyedUnarchiverDictionaryClass = Nil,
|
|
_CPKeyedUnarchiverArchiverValueClass = Nil;
|
|
|
|
/*
|
|
<objj>CPKeyedUnarchiver</objj> is used for creating objects out of
|
|
coded files or <objj>CPData</objj> objects that were created by
|
|
<objj>CPKeyedArchiver</objj>. More specifically, this class unarchives
|
|
objects from a data stream or file and brings them back into
|
|
memory for programmatic usage.
|
|
|
|
@delegate -(Class)unarchiver:(CPKeyedUnarchiver)unarchiver
|
|
cannotDecodeObjectOfClassName:(CPString)name
|
|
originalClasses:(CPArray)classNames;
|
|
Called when the specified class is not available during decoding.
|
|
The delegate may load the class, or return a substitute class
|
|
to use instead.
|
|
@param unarchiver the unarchiver performing the decode
|
|
@param name the name of the class that can't be found
|
|
@param an array of class names describing the encoded object's
|
|
class hierarchy. The first index is the encoded class name, and
|
|
each superclass is after that.
|
|
@return the <objj>Class</objj> to use instead or <code>nil</code>
|
|
to abort the unarchiving operation
|
|
|
|
@delegate -(id)unarchiver:(CPKeyedUnarchiver)unarchiver didDecodeObject:(id)object;
|
|
Called when the unarchiver decodes an object.
|
|
@param unarchiver the unarchiver doing the decoding
|
|
@param object the decoded objec
|
|
@return a substitute to use for the decoded object. This can be the same object argument provide,
|
|
another object or <code>nil</code>.
|
|
|
|
@delegate -(void)unarchiver:(CPKeyedUnarchiver)unarchiver willReplaceObject:(id)object withObject:(id)newObject;
|
|
Called when a decoded object has been substituted with another. (for example, from <code>unarchiver:didDecodeObject:</code>.
|
|
@param unarchiver the unarchiver that decoded the object
|
|
@param object the original decoded object
|
|
@param newObject the replacement object
|
|
|
|
@delegate -(void)unarchiverWillFinish:(CPKeyedUnarchiver)unarchiver;
|
|
Called when the unarchiver is about to finish decoding.
|
|
@param unarchiver the unarchiver that's about to finish
|
|
|
|
@delegate -(void)unarchiverDidFinish:(CPKeyedUnarchiver)unarchiver;
|
|
Called when the unarchiver has finished decoding.
|
|
@param unarchiver the unarchiver that finished decoding
|
|
*/
|
|
@implementation CPKeyedUnarchiver : CPCoder
|
|
{
|
|
id _delegate;
|
|
unsigned _delegateSelectors;
|
|
|
|
CPData _data;
|
|
|
|
// FIXME: We need to support this!
|
|
CPDictionary _replacementClassNames;
|
|
|
|
CPDictionary _objects;
|
|
CPDictionary _archive;
|
|
|
|
CPDictionary _plistObject;
|
|
CPArray _plistObjects;
|
|
}
|
|
|
|
/*
|
|
@ignore
|
|
*/
|
|
+ (void)initialize
|
|
{
|
|
if (self != [CPKeyedUnarchiver class])
|
|
return;
|
|
|
|
_CPKeyedUnarchiverArrayClass = [CPArray class];
|
|
_CPKeyedUnarchiverStringClass = [CPString class];
|
|
_CPKeyedUnarchiverDictionaryClass = [CPDictionary class];
|
|
_CPKeyedUnarchiverArchiverValueClass = [_CPKeyedArchiverValue class];
|
|
}
|
|
|
|
/*
|
|
Initializes the receiver to unarchive objects from the specified data object.
|
|
@param data the data stream from which to read objects
|
|
@return the initialized unarchiver
|
|
*/
|
|
- (id)initForReadingWithData:(CPData)data
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_archive = [data plistObject];
|
|
_objects = [CPArray arrayWithObject:[CPNull null]];
|
|
|
|
_plistObject = [_archive objectForKey:_CPKeyedArchiverTopKey];
|
|
_plistObjects = [_archive objectForKey:_CPKeyedArchiverObjectsKey];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
Unarchives the object graph in the provided data object.
|
|
@param data the data from which to read the graph
|
|
@return the unarchived object
|
|
*/
|
|
+ (id)unarchiveObjectWithData:(CPData)data
|
|
{
|
|
var unarchiver = [[self alloc] initForReadingWithData:data],
|
|
object = [unarchiver decodeObjectForKey:@"root"];
|
|
|
|
[unarchiver finishDecoding];
|
|
|
|
return object;
|
|
}
|
|
|
|
/*
|
|
Not implemented
|
|
*/
|
|
+ (id)unarchiveObjectWithFile:(CPString)aFilePath
|
|
{
|
|
}
|
|
|
|
/*
|
|
Not implemented
|
|
*/
|
|
+ (id)unarchiveObjectWithFile:(CPString)aFilePath asynchronously:(BOOL)aFlag
|
|
{
|
|
}
|
|
|
|
/*
|
|
Returns <code>YES</code> if an object exists for <code>aKey</code>.
|
|
@param aKey the object's associated key
|
|
*/
|
|
- (BOOL)containsValueForKey:(CPString)aKey
|
|
{
|
|
return [_plistObject objectForKey:aKey] != nil;
|
|
}
|
|
|
|
/* @ignore */
|
|
- (id)_decodeArrayOfObjectsForKey:(CPString)aKey
|
|
{
|
|
var object = [_plistObject objectForKey:aKey];
|
|
|
|
if ([object isKindOfClass:_CPKeyedUnarchiverArrayClass])
|
|
{
|
|
var index = 0,
|
|
count = object.length,
|
|
array = [];
|
|
|
|
for (; index < count; ++index)
|
|
array[index] = _CPKeyedUnarchiverDecodeObjectAtIndex(self, [object[index] objectForKey:_CPKeyedArchiverUIDKey]);
|
|
|
|
return array;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
/* @ignore */
|
|
- (void)_decodeDictionaryOfObjectsForKey:(CPString)aKey
|
|
{
|
|
var object = [_plistObject objectForKey:aKey];
|
|
|
|
if ([object isKindOfClass:_CPKeyedUnarchiverDictionaryClass])
|
|
{
|
|
var key,
|
|
keys = [object keyEnumerator],
|
|
dictionary = [CPDictionary dictionary];
|
|
|
|
while (key = [keys nextObject])
|
|
[dictionary setObject:_CPKeyedUnarchiverDecodeObjectAtIndex(self, [[object objectForKey:key] objectForKey:_CPKeyedArchiverUIDKey]) forKey:key];
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
Decodes a <code>BOOL</code> from the archive
|
|
@param aKey the <code>BOOL</code>'s associated key
|
|
@return the decoded <code>BOOL</code>
|
|
*/
|
|
- (BOOL)decodeBoolForKey:(CPString)aKey
|
|
{
|
|
return [self decodeObjectForKey:aKey];
|
|
}
|
|
|
|
/*
|
|
Decodes a <code>float</code> from the archive
|
|
@param aKey the <code>float</code>'s associated key
|
|
@return the decoded <code>float</code>
|
|
*/
|
|
- (float)decodeFloatForKey:(CPString)aKey
|
|
{
|
|
return [self decodeObjectForKey:aKey];
|
|
}
|
|
|
|
/*
|
|
Decodes a <code>double</code> from the archive.
|
|
@param aKey the <code>double</code>'s associated key
|
|
@return the decoded <code>double</code>
|
|
*/
|
|
- (double)decodeDoubleForKey:(CPString)aKey
|
|
{
|
|
return [self decodeObjectForKey:aKey];
|
|
}
|
|
|
|
/*
|
|
Decodes an <code>int</code> from the archive.
|
|
@param aKey the <code>int</code>'s associated key
|
|
@return the decoded <code>int</code>
|
|
*/
|
|
- (int)decodeIntForKey:(CPString)aKey
|
|
{
|
|
return [self decodeObjectForKey:aKey];
|
|
}
|
|
|
|
/*
|
|
Decodes a <objj>CGPoint</objj> from the archive.
|
|
@param aKey the point's associated key
|
|
@return the decoded point
|
|
*/
|
|
- (CGPoint)decodePointForKey:(CPString)aKey
|
|
{
|
|
var object = [self decodeObjectForKey:aKey];
|
|
|
|
if(object)
|
|
return CPPointFromString(object);
|
|
else
|
|
return CPPointMake(0.0, 0.0);
|
|
}
|
|
|
|
/*
|
|
Decodes a <objj>CGRect</objj> from the archive.
|
|
@param aKey the rectangle's associated key
|
|
@return the decoded rectangle
|
|
*/
|
|
- (CGRect)decodeRectForKey:(CPString)aKey
|
|
{
|
|
var object = [self decodeObjectForKey:aKey];
|
|
|
|
if(object)
|
|
return CPRectFromString(object);
|
|
else
|
|
return CPRectMakeZero();
|
|
}
|
|
|
|
/*
|
|
Decodes a <objj>CGSize</objj> from the archive.
|
|
@param aKey the size's associated key
|
|
@return the decoded size
|
|
*/
|
|
- (CGSize)decodeSizeForKey:(CPString)aKey
|
|
{
|
|
var object = [self decodeObjectForKey:aKey];
|
|
|
|
if(object)
|
|
return CPSizeFromString(object);
|
|
else
|
|
return CPSizeMake(0.0, 0.0);
|
|
}
|
|
|
|
/*
|
|
Decodes an object from the archive.
|
|
@param aKey the object's associated key
|
|
@return the decoded object
|
|
*/
|
|
- (id)decodeObjectForKey:(CPString)aKey
|
|
{
|
|
var object = [_plistObject objectForKey:aKey];
|
|
|
|
if ([object isKindOfClass:_CPKeyedUnarchiverDictionaryClass])
|
|
return _CPKeyedUnarchiverDecodeObjectAtIndex(self, [object objectForKey:_CPKeyedArchiverUIDKey]);
|
|
else if ([object isKindOfClass:[CPNumber class]])
|
|
return object;
|
|
/* else
|
|
alert([object className] + " " + object + " " + aKey + " " + [_plistObject description]);*/
|
|
|
|
return nil;
|
|
}
|
|
|
|
/*
|
|
Notifies the delegates that decoding has finished.
|
|
*/
|
|
- (void)finishDecoding
|
|
{
|
|
if (_delegateSelectors & _CPKeyedUnarchiverWillFinishSelector)
|
|
[_delegate unarchiverWillFinish:self];
|
|
|
|
if (_delegateSelectors & _CPKeyedUnarchiverDidFinishSelector)
|
|
[_delegate unarchiverDidFinish:self];
|
|
}
|
|
|
|
/*
|
|
Returns the keyed unarchiver's delegate
|
|
*/
|
|
- (id)delegate
|
|
{
|
|
return _delegate;
|
|
}
|
|
|
|
/*
|
|
Sets the unarchiver's delegate
|
|
@param the new delegate
|
|
*/
|
|
- (void)setDelegate:(id)aDelegate
|
|
{
|
|
_delegate = aDelegate;
|
|
|
|
if ([_delegate respondsToSelector:@selector(unarchiver:cannotDecodeObjectOfClassName:originalClasses:)])
|
|
_delegateSelectors |= _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector;
|
|
|
|
if ([_delegate respondsToSelector:@selector(unarchiver:didDecodeObject:)])
|
|
_delegateSelectors |= _CPKeyedUnarchiverDidDecodeObjectSelector;
|
|
|
|
if ([_delegate respondsToSelector:@selector(unarchiver:willReplaceObject:withObject:)])
|
|
_delegateSelectors |= _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector;
|
|
|
|
if ([_delegate respondsToSelector:@selector(unarchiverWillFinish:)])
|
|
_delegateSelectors |= _CPKeyedUnarchiverWilFinishSelector;
|
|
|
|
if ([_delegate respondsToSelector:@selector(unarchiverDidFinish:)])
|
|
_delegateSelectors |= _CPKeyedUnarchiverDidFinishSelector;
|
|
}
|
|
|
|
- (BOOL)allowsKeyedCoding
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex)
|
|
{
|
|
var object = self._objects[anIndex];
|
|
|
|
if (object)
|
|
if (object == self._objects[0])
|
|
return nil;
|
|
else
|
|
return object;
|
|
|
|
var object,
|
|
plistObject = self._plistObjects[anIndex];
|
|
|
|
if ([plistObject isKindOfClass:_CPKeyedUnarchiverDictionaryClass])
|
|
{
|
|
var plistClass = self._plistObjects[[[plistObject objectForKey:_CPKeyedArchiverClassKey] objectForKey:_CPKeyedArchiverUIDKey]],
|
|
className = [plistClass objectForKey:_CPKeyedArchiverClassNameKey],
|
|
classes = [plistClass objectForKey:_CPKeyedArchiverClassesKey],
|
|
theClass = CPClassFromString(className);
|
|
|
|
object = [theClass alloc];
|
|
|
|
// It is important to do this before calling initWithCoder so that decoding can be self referential (something = self).
|
|
self._objects[anIndex] = object;
|
|
|
|
var savedPlistObject = self._plistObject;
|
|
|
|
self._plistObject = plistObject;
|
|
|
|
var processedObject = [object initWithCoder:self];
|
|
|
|
self._plistObject = savedPlistObject;
|
|
|
|
if (processedObject != object)
|
|
{
|
|
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
|
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
|
|
|
object = processedObject;
|
|
self._objects[anIndex] = processedObject;
|
|
}
|
|
|
|
processedObject = [object awakeAfterUsingCoder:self];
|
|
|
|
if (processedObject == object)
|
|
{
|
|
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
|
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
|
|
|
object = processedObject;
|
|
self._objects[anIndex] = processedObject;
|
|
}
|
|
|
|
if (self._delegate)
|
|
{
|
|
if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector)
|
|
processedObject = [self._delegate unarchiver:self didDecodeObject:object];
|
|
|
|
if (processedObject != object)
|
|
{
|
|
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
|
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
|
|
|
object = processedObject;
|
|
self._objects[anIndex] = processedObject;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self._objects[anIndex] = object = plistObject;
|
|
|
|
if ([object class] == _CPKeyedUnarchiverStringClass)
|
|
{
|
|
if (object == _CPKeyedArchiverNullString)
|
|
{
|
|
self._objects[anIndex] = self._objects[0];
|
|
|
|
return nil;
|
|
}
|
|
else
|
|
self._objects[anIndex] = object = plistObject;
|
|
}
|
|
}
|
|
|
|
// If this object is a member of _CPKeyedArchiverValue, then we know
|
|
// that it is a wrapper for a primitive JavaScript object.
|
|
if ([object isMemberOfClass:_CPKeyedUnarchiverArchiverValueClass])
|
|
object = [object JSObject];
|
|
|
|
return object;
|
|
}
|
|
|