![]() |
API 0.9.5
|
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 }