API 0.9.5
Foundation/CPKeyedArchiver.j
Go to the documentation of this file.
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 }
 All Classes Files Functions Variables Defines