![]() |
API 0.9.5
|
00001 /* 00002 * CPKeyedArchiver.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 var CPArchiverReplacementClassNames = nil; 00025 00026 var _CPKeyedArchiverDidEncodeObjectSelector = 1, 00027 _CPKeyedArchiverWillEncodeObjectSelector = 2, 00028 _CPKeyedArchiverWillReplaceObjectWithObjectSelector = 4, 00029 _CPKeyedArchiverDidFinishSelector = 8, 00030 _CPKeyedArchiverWillFinishSelector = 16; 00031 00032 var _CPKeyedArchiverNullString = "$null", 00033 _CPKeyedArchiverNullReference = nil, 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 _CPKeyedArchiverStringClass = Nil, 00047 _CPKeyedArchiverNumberClass = Nil; 00048 00049 /* @ignore */ 00050 @implementation _CPKeyedArchiverValue : CPValue 00051 { 00052 id __doxygen__; 00053 } 00054 00055 @end 00056 00096 @implementation CPKeyedArchiver : CPCoder 00097 { 00098 id _delegate; 00099 unsigned _delegateSelectors; 00100 00101 CPData _data; 00102 00103 CPArray _objects; 00104 00105 CPDictionary _UIDs; 00106 CPDictionary _conditionalUIDs; 00107 00108 CPDictionary _replacementObjects; 00109 CPDictionary _replacementClassNames; 00110 00111 id _plistObject; 00112 CPMutableArray _plistObjects; 00113 00114 CPPropertyListFormat _outputFormat; 00115 } 00116 00117 /* 00118 @ignore 00119 */ 00120 + (void)initialize 00121 { 00122 if (self != [CPKeyedArchiver class]) 00123 return; 00124 00125 _CPKeyedArchiverStringClass = [CPString class]; 00126 _CPKeyedArchiverNumberClass = [CPNumber class]; 00127 00128 _CPKeyedArchiverNullReference = [CPDictionary dictionaryWithObject:0 forKey:_CPKeyedArchiverUIDKey]; 00129 } 00130 00131 + (BOOL)allowsKeyedCoding 00132 { 00133 return YES; 00134 } 00135 00141 + (CPData)archivedDataWithRootObject:(id)anObject 00142 { 00143 var data = [CPData dataWithPlistObject:nil], 00144 archiver = [[self alloc] initForWritingWithMutableData:data]; 00145 00146 [archiver encodeObject:anObject forKey:@"root"]; 00147 [archiver finishEncoding]; 00148 00149 return data; 00150 } 00151 00152 // Initializing a CPKeyedArchiver object 00158 - (id)initForWritingWithMutableData:(CPMutableData)data 00159 { 00160 self = [super init]; 00161 00162 if (self) 00163 { 00164 _data = data; 00165 00166 _objects = []; 00167 00168 _UIDs = [CPDictionary dictionary]; 00169 _conditionalUIDs = [CPDictionary dictionary]; 00170 00171 _replacementObjects = [CPDictionary dictionary]; 00172 00173 _data = data; 00174 00175 _plistObject = [CPDictionary dictionary]; 00176 _plistObjects = [CPArray arrayWithObject:_CPKeyedArchiverNullString]; 00177 } 00178 00179 return self; 00180 } 00181 00182 // Archiving Data 00187 - (void)finishEncoding 00188 { 00189 if (_delegate && _delegateSelectors & _CPKeyedArchiverWillFinishSelector) 00190 [_delegate archiverWillFinish:self]; 00191 00192 var i = 0, 00193 topObject = _plistObject, 00194 classes = []; 00195 00196 for (; i < _objects.length; ++i) 00197 { 00198 var object = _objects[i]; 00199 00200 // Do whatever with the class, yo. 00201 // We called willEncodeObject previously. 00202 00203 _plistObject = _plistObjects[[_UIDs objectForKey:[object UID]]]; 00204 [object encodeWithCoder:self]; 00205 00206 if (_delegate && _delegateSelectors & _CPKeyedArchiverDidEncodeObjectSelector) 00207 [_delegate archiver:self didEncodeObject:object]; 00208 } 00209 00210 _plistObject = [CPDictionary dictionary]; 00211 00212 [_plistObject setObject:topObject forKey:_CPKeyedArchiverTopKey]; 00213 [_plistObject setObject:_plistObjects forKey:_CPKeyedArchiverObjectsKey]; 00214 [_plistObject setObject:[self className] forKey:_CPKeyedArchiverArchiverKey]; 00215 [_plistObject setObject:@"100000" forKey:_CPKeyedArchiverVersionKey]; 00216 00217 [_data setPlistObject:_plistObject]; 00218 00219 if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishSelector) 00220 [_delegate archiverDidFinish:self]; 00221 } 00222 00226 - (CPPropertyListFormat)outputFormat 00227 { 00228 return _outputFormat; 00229 } 00230 00235 - (void)setOutputFormat:(CPPropertyListFormat)aPropertyListFormat 00236 { 00237 _outputFormat = aPropertyListFormat; 00238 } 00239 00245 - (void)encodeBool:(BOOL)aBOOL forKey:(CPString)aKey 00246 { 00247 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aBOOL, NO) forKey:aKey]; 00248 } 00249 00255 - (void)encodeDouble:(double)aDouble forKey:(CPString)aKey 00256 { 00257 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aDouble, NO) forKey:aKey]; 00258 } 00259 00265 - (void)encodeFloat:(float)aFloat forKey:(CPString)aKey 00266 { 00267 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aFloat, NO) forKey:aKey]; 00268 } 00269 00275 - (void)encodeInt:(float)anInt forKey:(CPString)aKey 00276 { 00277 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anInt, NO) forKey:aKey]; 00278 } 00279 00280 // Managing Delegates 00284 - (void)setDelegate:(id)aDelegate 00285 { 00286 _delegate = aDelegate; 00287 00288 if ([_delegate respondsToSelector:@selector(archiver:didEncodeObject:)]) 00289 _delegateSelectors |= _CPKeyedArchiverDidEncodeObjectSelector; 00290 00291 if ([_delegate respondsToSelector:@selector(archiver:willEncodeObject:)]) 00292 _delegateSelectors |= _CPKeyedArchiverWillEncodeObjectSelector; 00293 00294 if ([_delegate respondsToSelector:@selector(archiver:willReplaceObject:withObject:)]) 00295 _delegateSelectors |= _CPKeyedArchiverWillReplaceObjectWithObjectSelector; 00296 00297 if ([_delegate respondsToSelector:@selector(archiver:didFinishEncoding:)]) 00298 _delegateSelectors |= _CPKeyedArchiverDidFinishEncodingSelector; 00299 00300 if ([_delegate respondsToSelector:@selector(archiver:willFinishEncoding:)]) 00301 _delegateSelectors |= _CPKeyedArchiverWillFinishEncodingSelector; 00302 00303 } 00304 00308 - (id)delegate 00309 { 00310 return _delegate; 00311 } 00312 00318 - (void)encodePoint:(CGPoint)aPoint forKey:(CPString)aKey 00319 { 00320 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromPoint(aPoint), NO) forKey:aKey]; 00321 } 00322 00328 - (void)encodeRect:(CGRect)aRect forKey:(CPString)aKey 00329 { 00330 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromRect(aRect), NO) forKey:aKey]; 00331 } 00332 00338 - (void)encodeSize:(CGSize)aSize forKey:(CPString)aKey 00339 { 00340 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromSize(aSize), NO) forKey:aKey]; 00341 } 00342 00350 - (void)encodeConditionalObject:(id)anObject forKey:(CPString)aKey 00351 { 00352 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, YES) forKey:aKey]; 00353 } 00354 00360 - (void)encodeNumber:(CPNumber)aNumber forKey:(CPString)aKey 00361 { 00362 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aNumber, NO) forKey:aKey]; 00363 } 00364 00370 - (void)encodeObject:(id)anObject forKey:(CPString)aKey 00371 { 00372 [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, NO) forKey:aKey]; 00373 } 00374 00375 /* @ignore */ 00376 - (void)_encodeArrayOfObjects:(CPArray)objects forKey:(CPString)aKey 00377 { 00378 var i = 0, 00379 count = objects.length, 00380 references = []; 00381 00382 for (; i < count; ++i) 00383 [references addObject:_CPKeyedArchiverEncodeObject(self, objects[i], NO)]; 00384 00385 [_plistObject setObject:references forKey:aKey]; 00386 } 00387 00388 /* @ignore */ 00389 - (void)_encodeDictionaryOfObjects:(CPDictionary)aDictionary forKey:(CPString)aKey 00390 { 00391 var key, 00392 keys = [aDictionary keyEnumerator], 00393 references = [CPDictionary dictionary]; 00394 00395 while (key = [keys nextObject]) 00396 [references setObject:_CPKeyedArchiverEncodeObject(self, [aDictionary objectForKey:key], NO) forKey:key]; 00397 00398 [_plistObject setObject:references forKey:aKey]; 00399 } 00400 00401 // Managing classes and class names 00409 + (void)setClassName:(CPString)aClassName forClass:(Class)aClass 00410 { 00411 if (!CPArchiverReplacementClassNames) 00412 CPArchiverReplacementClassNames = [CPDictionary dictionary]; 00413 00414 [CPArchiverReplacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)]; 00415 } 00416 00424 + (CPString)classNameForClass:(Class)aClass 00425 { 00426 if (!CPArchiverReplacementClassNames) 00427 return aClass.name; 00428 00429 var className = [CPArchiverReplacementClassNames objectForKey:CPStringFromClass(aClass)]; 00430 00431 return className ? className : aClass.name; 00432 } 00433 00441 - (void)setClassName:(CPString)aClassName forClass:(Class)aClass 00442 { 00443 if (!_replacementClassNames) 00444 _replacementClassNames = [CPDictionary dictionary]; 00445 00446 [_replacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)]; 00447 } 00448 00454 - (CPString)classNameForClass:(Class)aClass 00455 { 00456 if (!_replacementClassNames) 00457 return aClass.name; 00458 00459 var className = [_replacementClassNames objectForKey:CPStringFromClass(aClassName)]; 00460 00461 return className ? className : aClass.name; 00462 } 00463 00464 @end 00465 00466 var _CPKeyedArchiverEncodeObject = function(self, anObject, isConditional) 00467 { 00468 // We wrap primitive JavaScript objects in a unique subclass of CPValue. 00469 // This way, when we unarchive, we know to unwrap it, since 00470 // _CPKeyedArchiverValue should not be used anywhere else. 00471 if (anObject !== nil && anObject !== undefined && !anObject.isa) 00472 anObject = [_CPKeyedArchiverValue valueWithJSObject:anObject]; 00473 00474 // Get the proper replacement object 00475 var GUID = [anObject UID], 00476 object = [self._replacementObjects objectForKey:GUID]; 00477 00478 // If a replacement object doesn't exist, then actually ask for one. 00479 // Explicitly compare to nil because object could be === 0. 00480 if (object === nil) 00481 { 00482 object = [anObject replacementObjectForKeyedArchiver:self]; 00483 00484 // Notify our delegate of this replacement. 00485 if (self._delegate) 00486 { 00487 if (object !== anObject && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector) 00488 [self._delegate archiver:self willReplaceObject:anObject withObject:object]; 00489 00490 if (self._delegateSelectors & _CPKeyedArchiverWillEncodeObjectSelector) 00491 { 00492 anObject = [self._delegate archiver:self willEncodeObject:object]; 00493 00494 if (anObject !== object && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector) 00495 [self._delegate archiver:self willReplaceObject:object withObject:anObject]; 00496 00497 object = anObject; 00498 } 00499 } 00500 00501 [self._replacementObjects setObject:object forKey:GUID]; 00502 } 00503 00504 // If we still don't have an object by this point, then return a 00505 // reference to the null object. 00506 // Explicitly compare to nil because object could be === 0. 00507 if (object === nil) 00508 return _CPKeyedArchiverNullReference; 00509 00510 // If not, then grab the object's UID 00511 var UID = [self._UIDs objectForKey:GUID = [object UID]]; 00512 00513 // If this object doesn't have a unique index in the object table yet, 00514 // then it also hasn't been properly encoded. We explicitly compare 00515 // index to nil since it could be 0, which would also evaluate to false. 00516 if (UID === nil) 00517 { 00518 // If it is being conditionally encoded, then 00519 if (isConditional) 00520 { 00521 // If we haven't already noted this conditional object... 00522 if ((UID = [self._conditionalUIDs objectForKey:GUID]) === nil) 00523 { 00524 // Use the null object as a placeholder. 00525 [self._conditionalUIDs setObject:UID = [self._plistObjects count] forKey:GUID]; 00526 [self._plistObjects addObject:_CPKeyedArchiverNullString]; 00527 } 00528 } 00529 else 00530 { 00531 var theClass = [object classForKeyedArchiver], 00532 plistObject = nil; 00533 00534 if ((theClass === _CPKeyedArchiverStringClass) || (theClass === _CPKeyedArchiverNumberClass))// || theClass == _CPKeyedArchiverBooleanClass) 00535 plistObject = object; 00536 else 00537 { 00538 // Only actually encode the object and create a plist representation if it is not a simple type. 00539 plistObject = [CPDictionary dictionary]; 00540 00541 [self._objects addObject:object]; 00542 00543 var className = [self classNameForClass:theClass]; 00544 00545 if (!className) 00546 className = [[self class] classNameForClass:theClass]; 00547 00548 if (!className) 00549 className = theClass.name; 00550 else 00551 theClass = CPClassFromString(className); 00552 00553 var classUID = [self._UIDs objectForKey:className]; 00554 00555 if (!classUID) 00556 { 00557 var plistClass = [CPDictionary dictionary], 00558 hierarchy = []; 00559 00560 [plistClass setObject:className forKey:_CPKeyedArchiverClassNameKey]; 00561 00562 do 00563 { 00564 [hierarchy addObject:CPStringFromClass(theClass)]; 00565 } while (theClass = [theClass superclass]); 00566 00567 [plistClass setObject:hierarchy forKey:_CPKeyedArchiverClassesKey]; 00568 00569 classUID = [self._plistObjects count]; 00570 [self._plistObjects addObject:plistClass]; 00571 [self._UIDs setObject:classUID forKey:className]; 00572 } 00573 00574 [plistObject setObject:[CPDictionary dictionaryWithObject:classUID forKey:_CPKeyedArchiverUIDKey] forKey:_CPKeyedArchiverClassKey]; 00575 } 00576 00577 UID = [self._conditionalUIDs objectForKey:GUID]; 00578 00579 // If this object WAS previously encoded conditionally... 00580 if (UID !== nil) 00581 { 00582 [self._UIDs setObject:UID forKey:GUID]; 00583 [self._plistObjects replaceObjectAtIndex:UID withObject:plistObject]; 00584 } 00585 else 00586 { 00587 [self._UIDs setObject:UID = [self._plistObjects count] forKey:GUID]; 00588 [self._plistObjects addObject:plistObject]; 00589 } 00590 } 00591 } 00592 00593 return [CPDictionary dictionaryWithObject:UID forKey:_CPKeyedArchiverUIDKey]; 00594 }