API 0.9.5
Foundation/CPKeyedUnarchiver.j
Go to the documentation of this file.
00001 /*
00002  * CPKeyedUnarchiver.j
00003  * Foundation
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 CPInvalidUnarchiveOperationException    = @"CPInvalidUnarchiveOperationException";
00025 
00026 var _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector              = 1 << 0,
00027     _CPKeyedUnarchiverDidDecodeObjectSelector                                           = 1 << 1,
00028     _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector                               = 1 << 2,
00029     _CPKeyedUnarchiverWillFinishSelector                                                = 1 << 3,
00030     _CPKeyedUnarchiverDidFinishSelector                                                 = 1 << 4,
00031     CPKeyedUnarchiverDelegate_unarchiver_cannotDecodeObjectOfClassName_originalClasses_ = 1 << 5;
00032 
00033 var _CPKeyedArchiverNullString                                              = "$null",
00034 
00035     _CPKeyedArchiverUIDKey                                                  = "CP$UID",
00036 
00037     _CPKeyedArchiverTopKey                                                  = "$top",
00038     _CPKeyedArchiverObjectsKey                                              = "$objects",
00039     _CPKeyedArchiverArchiverKey                                             = "$archiver",
00040     _CPKeyedArchiverVersionKey                                              = "$version",
00041 
00042     _CPKeyedArchiverClassNameKey                                            = "$classname",
00043     _CPKeyedArchiverClassesKey                                              = "$classes",
00044     _CPKeyedArchiverClassKey                                                = "$class";
00045 
00046 var CPArrayClass                                                            = Nil,
00047     CPMutableArrayClass                                                     = Nil,
00048     CPStringClass                                                           = Nil,
00049     CPDictionaryClass                                                       = Nil,
00050     CPMutableDictionaryClass                                                = Nil,
00051     CPNumberClass                                                           = Nil,
00052     CPDataClass                                                             = Nil,
00053     _CPKeyedArchiverValueClass                                              = Nil;
00054 
00101 @implementation CPKeyedUnarchiver : CPCoder
00102 {
00103     id              _delegate;
00104     unsigned        _delegateSelectors;
00105 
00106     CPData          _data;
00107 
00108     CPDictionary    _replacementClasses;
00109 
00110     CPArray         _objects;
00111     CPDictionary    _archive;
00112 
00113     CPDictionary    _plistObject;
00114     CPArray         _plistObjects;
00115 }
00116 
00117 /*
00118     @ignore
00119 */
00120 + (void)initialize
00121 {
00122     if (self !== [CPKeyedUnarchiver class])
00123         return;
00124 
00125     CPArrayClass = [CPArray class];
00126     CPMutableArrayClass = [CPMutableArray class];
00127     CPStringClass = [CPString class];
00128     CPDictionaryClass = [CPDictionary class];
00129     CPMutableDictionaryClass = [CPMutableDictionary class];
00130     CPNumberClass = [CPNumber class];
00131     CPDataClass = [CPData class];
00132     _CPKeyedArchiverValueClass = [_CPKeyedArchiverValue class];
00133 }
00134 
00135 /*
00136     Initializes the receiver to unarchive objects from the specified data object.
00137     @param data the data stream from which to read objects
00138     @return the initialized unarchiver
00139 */
00140 - (id)initForReadingWithData:(CPData)data
00141 {
00142     self = [super init];
00143 
00144     if (self)
00145     {
00146         _archive = [data plistObject];
00147         _objects = [[CPNull null]];
00148 
00149         _plistObject = [_archive objectForKey:_CPKeyedArchiverTopKey];
00150         _plistObjects = [_archive objectForKey:_CPKeyedArchiverObjectsKey];
00151 
00152         _replacementClasses = new CFMutableDictionary();
00153     }
00154 
00155     return self;
00156 }
00157 
00158 /*
00159     Unarchives the object graph in the provided data object.
00160     @param data the data from which to read the graph
00161     @return the unarchived object
00162 */
00163 + (id)unarchiveObjectWithData:(CPData)aData
00164 {
00165     if (!aData)
00166     {
00167         CPLog.error("Null data passed to -[CPKeyedUnarchiver unarchiveObjectWithData:].");
00168         return nil;
00169     }
00170 
00171     var unarchiver = [[self alloc] initForReadingWithData:aData],
00172         object = [unarchiver decodeObjectForKey:@"root"];
00173 
00174     [unarchiver finishDecoding];
00175 
00176     return object;
00177 }
00178 
00179 /*
00180     Not implemented
00181 */
00182 + (id)unarchiveObjectWithFile:(CPString)aFilePath
00183 {
00184 }
00185 
00186 /*
00187     Not implemented
00188 */
00189 + (id)unarchiveObjectWithFile:(CPString)aFilePath asynchronously:(BOOL)aFlag
00190 {
00191 }
00192 
00193 /*
00194     Returns \c YES if an object exists for \c aKey.
00195     @param aKey the object's associated key
00196 */
00197 - (BOOL)containsValueForKey:(CPString)aKey
00198 {
00199     return _plistObject.valueForKey(aKey) != nil;
00200 }
00201 
00202 /* @ignore */
00203 - (CPDictionary)_decodeDictionaryOfObjectsForKey:(CPString)aKey
00204 {
00205     var object = _plistObject.valueForKey(aKey),
00206         objectClass = (object != nil) && object.isa;
00207 
00208     if (objectClass === CPDictionaryClass || objectClass === CPMutableDictionaryClass)
00209     {
00210         var keys = object.keys(),
00211             index = 0,
00212             count = keys.length,
00213             dictionary = new CFMutableDictionary();
00214 
00215         for (; index < count; ++index)
00216         {
00217             var key = keys[index];
00218 
00219             dictionary.setValueForKey(key, _CPKeyedUnarchiverDecodeObjectAtIndex(self, object.valueForKey(key).valueForKey(_CPKeyedArchiverUIDKey)));
00220         }
00221 
00222         return dictionary;
00223     }
00224 
00225     return nil;
00226 }
00227 
00228 /*
00229     Decodes a \c BOOL from the archive
00230     @param aKey the \c BOOL's associated key
00231     @return the decoded \c BOOL
00232 */
00233 - (BOOL)decodeBoolForKey:(CPString)aKey
00234 {
00235     return !![self decodeObjectForKey:aKey];
00236 }
00237 
00238 /*
00239     Decodes a \c float from the archive
00240     @param aKey the \c float's associated key
00241     @return the decoded \c float
00242 */
00243 - (float)decodeFloatForKey:(CPString)aKey
00244 {
00245     return [self decodeObjectForKey:aKey];
00246 }
00247 
00248 /*
00249     Decodes a \c double from the archive.
00250     @param aKey the \c double's associated key
00251     @return the decoded \c double
00252 */
00253 - (double)decodeDoubleForKey:(CPString)aKey
00254 {
00255     return [self decodeObjectForKey:aKey];
00256 }
00257 
00258 /*
00259     Decodes an \c int from the archive.
00260     @param aKey the \c int's associated key
00261     @return the decoded \c int
00262 */
00263 - (int)decodeIntForKey:(CPString)aKey
00264 {
00265     return [self decodeObjectForKey:aKey];
00266 }
00267 
00268 /*
00269     Decodes a CGPoint from the archive.
00270     @param aKey the point's associated key
00271     @return the decoded point
00272 */
00273 - (CGPoint)decodePointForKey:(CPString)aKey
00274 {
00275     var object = [self decodeObjectForKey:aKey];
00276 
00277     if (object)
00278         return CPPointFromString(object);
00279     else
00280         return CPPointMake(0.0, 0.0);
00281 }
00282 
00283 /*
00284     Decodes a CGRect from the archive.
00285     @param aKey the rectangle's associated key
00286     @return the decoded rectangle
00287 */
00288 - (CGRect)decodeRectForKey:(CPString)aKey
00289 {
00290     var object = [self decodeObjectForKey:aKey];
00291 
00292     if (object)
00293         return CPRectFromString(object);
00294     else
00295         return CPRectMakeZero();
00296 }
00297 
00298 /*
00299     Decodes a CGSize from the archive.
00300     @param aKey the size's associated key
00301     @return the decoded size
00302 */
00303 - (CGSize)decodeSizeForKey:(CPString)aKey
00304 {
00305     var object = [self decodeObjectForKey:aKey];
00306 
00307     if (object)
00308         return CPSizeFromString(object);
00309     else
00310         return CPSizeMake(0.0, 0.0);
00311 }
00312 
00313 /*
00314     Decodes an object from the archive.
00315     @param aKey the object's associated key
00316     @return the decoded object
00317 */
00318 - (id)decodeObjectForKey:(CPString)aKey
00319 {
00320     var object = _plistObject.valueForKey(aKey),
00321         objectClass = (object != nil) && object.isa;
00322 
00323     if (objectClass === CPDictionaryClass || objectClass === CPMutableDictionaryClass)
00324         return _CPKeyedUnarchiverDecodeObjectAtIndex(self, object.valueForKey(_CPKeyedArchiverUIDKey));
00325 
00326     else if (objectClass === CPNumberClass || objectClass === CPDataClass || objectClass === CPStringClass)
00327         return object;
00328 
00329     else if (objectClass === _CPJavaScriptArray)
00330     {
00331         var index = 0,
00332             count = object.length,
00333             array = [];
00334 
00335         for (; index < count; ++index)
00336             array[index] = _CPKeyedUnarchiverDecodeObjectAtIndex(self, object[index].valueForKey(_CPKeyedArchiverUIDKey));
00337 
00338         return array;
00339     }
00340 /*    else
00341         CPLog([object className] + " " + object + " " + aKey + " " + [_plistObject description]);*/
00342 
00343     return nil;
00344 }
00345 
00346 /*
00347     Decodes bytes from the archive.
00348     @param aKey the object's associated key
00349     @return array of bytes
00350 */
00351 - (id)decodeBytesForKey:(CPString)aKey
00352 {
00353     // We get the CPData wrapper, then extract the bytes array
00354     var data = [self decodeObjectForKey:aKey];
00355 
00356     if (!data)
00357         return nil;
00358 
00359     var objectClass = data.isa;
00360     if (objectClass === CPDataClass)
00361         return data.bytes;
00362 
00363     return nil;
00364 }
00365 
00366 /*
00367     Notifies the delegates that decoding has finished.
00368 */
00369 - (void)finishDecoding
00370 {
00371     if (_delegateSelectors & _CPKeyedUnarchiverWillFinishSelector)
00372         [_delegate unarchiverWillFinish:self];
00373 
00374     if (_delegateSelectors & _CPKeyedUnarchiverDidFinishSelector)
00375         [_delegate unarchiverDidFinish:self];
00376 }
00377 
00378 /*
00379     Returns the keyed unarchiver's delegate
00380 */
00381 - (id)delegate
00382 {
00383     return _delegate;
00384 }
00385 
00386 /*
00387     Sets the unarchiver's delegate
00388     @param the new delegate
00389 */
00390 - (void)setDelegate:(id)aDelegate
00391 {
00392     _delegate = aDelegate;
00393 
00394     if ([_delegate respondsToSelector:@selector(unarchiver:cannotDecodeObjectOfClassName:originalClasses:)])
00395         _delegateSelectors |= _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector;
00396 
00397     if ([_delegate respondsToSelector:@selector(unarchiver:didDecodeObject:)])
00398         _delegateSelectors |= _CPKeyedUnarchiverDidDecodeObjectSelector;
00399 
00400     if ([_delegate respondsToSelector:@selector(unarchiver:willReplaceObject:withObject:)])
00401         _delegateSelectors |= _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector;
00402 
00403     if ([_delegate respondsToSelector:@selector(unarchiverWillFinish:)])
00404         _delegateSelectors |= _CPKeyedUnarchiverWilFinishSelector;
00405 
00406     if ([_delegate respondsToSelector:@selector(unarchiverDidFinish:)])
00407         _delegateSelectors |= _CPKeyedUnarchiverDidFinishSelector;
00408 
00409     if ([_delegate respondsToSelector:@selector(unarchiver:cannotDecodeObjectOfClassName:originalClasses:)])
00410         _delegateSelectors |= CPKeyedUnarchiverDelegate_unarchiver_cannotDecodeObjectOfClassName_originalClasses_;
00411 }
00412 
00413 - (void)setClass:(Class)aClass forClassName:(CPString)aClassName
00414 {
00415     _replacementClasses.setValueForKey(aClassName, aClass);
00416 }
00417 
00418 - (Class)classForClassName:(CPString)aClassName
00419 {
00420     return _replacementClasses.valueForKey(aClassName);
00421 }
00422 
00423 - (BOOL)allowsKeyedCoding
00424 {
00425     return YES;
00426 }
00427 
00428 @end
00429 
00430 var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex)
00431 {
00432     var object = self._objects[anIndex];
00433 
00434     if (object)
00435     {
00436         if (object === self._objects[0])
00437             return nil;
00438         // Don't return immediately here. The _CPKeyedArchiverValueClass unwrapper code
00439         // hasn't executed yet.
00440     }
00441     else
00442     {
00443         var plistObject = self._plistObjects[anIndex],
00444             plistObjectClass = plistObject.isa;
00445 
00446         if (plistObjectClass === CPDictionaryClass || plistObjectClass === CPMutableDictionaryClass)
00447         {
00448             var plistClass = self._plistObjects[plistObject.valueForKey(_CPKeyedArchiverClassKey).valueForKey(_CPKeyedArchiverUIDKey)],
00449                 className = plistClass.valueForKey(_CPKeyedArchiverClassNameKey),
00450                 classes = plistClass.valueForKey(_CPKeyedArchiverClassesKey),
00451                 theClass = [self classForClassName:className];
00452 
00453             if (!theClass)
00454                 theClass = CPClassFromString(className);
00455 
00456             if (!theClass && (self._delegateSelectors & CPKeyedUnarchiverDelegate_unarchiver_cannotDecodeObjectOfClassName_originalClasses_))
00457                 theClass = [_delegate unarchiver:self cannotDecodeObjectOfClassName:className originalClasses:classes];
00458 
00459             if (!theClass)
00460                 [CPException raise:CPInvalidUnarchiveOperationException reason:@"-[CPKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (" + className + @")"];
00461 
00462             var savedPlistObject = self._plistObject;
00463 
00464             self._plistObject = plistObject;
00465 
00466             // Should we only call this on _CPCibClassSwapper? (currently the only class that makes use of this).
00467             object = [theClass allocWithCoder:self];
00468 
00469             // It is important to do this before calling initWithCoder so that decoding can be self referential (something = self).
00470             self._objects[anIndex] = object;
00471 
00472             var processedObject = [object initWithCoder:self];
00473 
00474             self._plistObject = savedPlistObject;
00475 
00476             if (processedObject !== object)
00477             {
00478                 if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
00479                     [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
00480 
00481                 object = processedObject;
00482                 self._objects[anIndex] = processedObject;
00483             }
00484 
00485             processedObject = [object awakeAfterUsingCoder:self];
00486 
00487             if (processedObject !== object)
00488             {
00489                 if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
00490                     [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
00491 
00492                 object = processedObject;
00493                 self._objects[anIndex] = processedObject;
00494             }
00495 
00496             if (self._delegate)
00497             {
00498                 if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector)
00499                     processedObject = [self._delegate unarchiver:self didDecodeObject:object];
00500 
00501                 if (processedObject && processedObject != object)
00502                 {
00503                     if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
00504                         [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
00505 
00506                     object = processedObject;
00507                     self._objects[anIndex] = processedObject;
00508                 }
00509             }
00510         }
00511         else
00512         {
00513             self._objects[anIndex] = object = plistObject;
00514 
00515             if ([object class] === CPStringClass)
00516             {
00517                 if (object === _CPKeyedArchiverNullString)
00518                 {
00519                     self._objects[anIndex] = self._objects[0];
00520 
00521                     return nil;
00522                 }
00523                 else
00524                     self._objects[anIndex] = object = plistObject;
00525             }
00526         }
00527     }
00528 
00529     // If this object is a member of _CPKeyedArchiverValue, then we know
00530     // that it is a wrapper for a primitive JavaScript object.
00531     if ((object != nil) && (object.isa === _CPKeyedArchiverValueClass))
00532         object = [object JSObject];
00533 
00534     return object;
00535 }
 All Classes Files Functions Variables Defines