API  0.9.10
CPKeyedArchiver.j
Go to the documentation of this file.
1 /*
2  * CPKeyedArchiver.j
3  * Foundation
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
25 
26 var _CPKeyedArchiverDidEncodeObjectSelector = 1,
27  _CPKeyedArchiverWillEncodeObjectSelector = 2,
28  _CPKeyedArchiverWillReplaceObjectWithObjectSelector = 4,
29  _CPKeyedArchiverDidFinishEncodingSelector = 8,
30  _CPKeyedArchiverWillFinishEncodingSelector = 16;
31 
32 var _CPKeyedArchiverNullString = "$null",
33  _CPKeyedArchiverNullReference = nil,
34 
35  _CPKeyedArchiverUIDKey = "CP$UID",
36 
37  _CPKeyedArchiverTopKey = "$top",
38  _CPKeyedArchiverObjectsKey = "$objects",
39  _CPKeyedArchiverArchiverKey = "$archiver",
40  _CPKeyedArchiverVersionKey = "$version",
41 
42  _CPKeyedArchiverClassNameKey = "$classname",
43  _CPKeyedArchiverClassesKey = "$classes",
44  _CPKeyedArchiverClassKey = "$class";
45 
46 var _CPKeyedArchiverStringClass = Nil,
47  _CPKeyedArchiverNumberClass = Nil;
48 
49 /* @ignore */
50 @implementation _CPKeyedArchiverValue : CPValue
51 {
52  id __doxygen__;
53 }
54 
55 @end
56 
95 @implementation CPKeyedArchiver : CPCoder
96 {
97  id _delegate;
98  unsigned _delegateSelectors;
99 
100  CPData _data;
101 
102  CPArray _objects;
103 
104  CPDictionary _UIDs;
105  CPDictionary _conditionalUIDs;
106 
107  CPDictionary _replacementObjects;
108  CPDictionary _replacementClassNames;
109 
110  id _plistObject;
111  CPMutableArray _plistObjects;
112 
113  CPPropertyListFormat _outputFormat;
114 }
115 
116 /*
117  @ignore
118 */
119 + (void)initialize
120 {
121  if (self !== [CPKeyedArchiver class])
122  return;
123 
124  _CPKeyedArchiverStringClass = [CPString class];
125  _CPKeyedArchiverNumberClass = [CPNumber class];
126 
127  _CPKeyedArchiverNullReference = @{ _CPKeyedArchiverUIDKey: 0 };
128 }
129 
130 + (BOOL)allowsKeyedCoding
131 {
132  return YES;
133 }
134 
140 + (CPData)archivedDataWithRootObject:(id)anObject
141 {
142  var data = [CPData dataWithPlistObject:nil],
143  archiver = [[self alloc] initForWritingWithMutableData:data];
144 
145  [archiver encodeObject:anObject forKey:@"root"];
146  [archiver finishEncoding];
147 
148  return data;
149 }
150 
151 // Initializing a CPKeyedArchiver object
157 - (id)initForWritingWithMutableData:(CPMutableData)data
158 {
159  self = [super init];
160 
161  if (self)
162  {
163  _data = data;
164 
165  _objects = [];
166 
167  _UIDs = @{};
168  _conditionalUIDs = @{};
169 
170  _replacementObjects = @{};
171 
172  _plistObject = @{};
173  _plistObjects = [CPArray arrayWithObject:_CPKeyedArchiverNullString];
174  }
175 
176  return self;
177 }
178 
179 // Archiving Data
184 - (void)finishEncoding
185 {
186  if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishEncodingSelector)
187  [_delegate archiverWillFinish:self];
188 
189  var i = 0,
190  topObject = _plistObject,
191  classes = [];
192 
193  for (; i < _objects.length; ++i)
194  {
195  var object = _objects[i];
196 
197  // Do whatever with the class, yo.
198  // We called willEncodeObject previously.
199 
200  _plistObject = _plistObjects[[_UIDs objectForKey:[object UID]]];
201  [object encodeWithCoder:self];
202 
203  if (_delegate && _delegateSelectors & _CPKeyedArchiverDidEncodeObjectSelector)
204  [_delegate archiver:self didEncodeObject:object];
205  }
206 
207  _plistObject = @{};
208 
209  [_plistObject setObject:topObject forKey:_CPKeyedArchiverTopKey];
210  [_plistObject setObject:_plistObjects forKey:_CPKeyedArchiverObjectsKey];
211  [_plistObject setObject:[self className] forKey:_CPKeyedArchiverArchiverKey];
212  [_plistObject setObject:@"100000" forKey:_CPKeyedArchiverVersionKey];
213 
214  [_data setPlistObject:_plistObject];
215 
216  if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishEncodingSelector)
217  [_delegate archiverDidFinish:self];
218 }
219 
223 - (CPPropertyListFormat)outputFormat
224 {
225  return _outputFormat;
226 }
227 
232 - (void)setOutputFormat:(CPPropertyListFormat)aPropertyListFormat
233 {
234  _outputFormat = aPropertyListFormat;
235 }
236 
242 - (void)encodeBool:(BOOL)aBOOL forKey:(CPString)aKey
243 {
244  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aBOOL, NO) forKey:aKey];
245 }
246 
252 - (void)encodeDouble:(double)aDouble forKey:(CPString)aKey
253 {
254  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aDouble, NO) forKey:aKey];
255 }
256 
262 - (void)encodeFloat:(float)aFloat forKey:(CPString)aKey
263 {
264  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aFloat, NO) forKey:aKey];
265 }
266 
272 - (void)encodeInt:(float)anInt forKey:(CPString)aKey
273 {
274  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anInt, NO) forKey:aKey];
275 }
276 
277 // Managing Delegates
281 - (void)setDelegate:(id)aDelegate
282 {
283  _delegate = aDelegate;
284 
285  if ([_delegate respondsToSelector:@selector(archiver:didEncodeObject:)])
286  _delegateSelectors |= _CPKeyedArchiverDidEncodeObjectSelector;
287 
288  if ([_delegate respondsToSelector:@selector(archiver:willEncodeObject:)])
289  _delegateSelectors |= _CPKeyedArchiverWillEncodeObjectSelector;
290 
291  if ([_delegate respondsToSelector:@selector(archiver:willReplaceObject:withObject:)])
292  _delegateSelectors |= _CPKeyedArchiverWillReplaceObjectWithObjectSelector;
293 
294  if ([_delegate respondsToSelector:@selector(archiver:didFinishEncoding:)])
295  _delegateSelectors |= _CPKeyedArchiverDidFinishEncodingSelector;
296 
297  if ([_delegate respondsToSelector:@selector(archiver:willFinishEncoding:)])
298  _delegateSelectors |= _CPKeyedArchiverWillFinishEncodingSelector;
299 
300 }
301 
305 - (id)delegate
306 {
307  return _delegate;
308 }
309 
315 - (void)encodePoint:(CGPoint)aPoint forKey:(CPString)aKey
316 {
317  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CGStringFromPoint(aPoint), NO) forKey:aKey];
318 }
319 
325 - (void)encodeRect:(CGRect)aRect forKey:(CPString)aKey
326 {
327  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CGStringFromRect(aRect), NO) forKey:aKey];
328 }
329 
335 - (void)encodeSize:(CGSize)aSize forKey:(CPString)aKey
336 {
337  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CGStringFromSize(aSize), NO) forKey:aKey];
338 }
339 
347 - (void)encodeConditionalObject:(id)anObject forKey:(CPString)aKey
348 {
349  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, YES) forKey:aKey];
350 }
351 
357 - (void)encodeNumber:(CPNumber)aNumber forKey:(CPString)aKey
358 {
359  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aNumber, NO) forKey:aKey];
360 }
361 
367 - (void)encodeObject:(id)anObject forKey:(CPString)aKey
368 {
369  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, NO) forKey:aKey];
370 }
371 
372 /* @ignore */
373 - (void)_encodeArrayOfObjects:(CPArray)objects forKey:(CPString)aKey
374 {
375  var references = [objects arrayByApplyingBlock:function(object)
376  {
377  return _CPKeyedArchiverEncodeObject(self, object, NO);
378  }];
379 
380  [_plistObject setObject:references forKey:aKey];
381 }
382 
383 /* @ignore */
384 - (void)_encodeDictionaryOfObjects:(CPDictionary)aDictionary forKey:(CPString)aKey
385 {
386  var key,
387  keys = [aDictionary keyEnumerator],
388  references = @{};
389 
390  while ((key = [keys nextObject]) !== nil)
391  [references setObject:_CPKeyedArchiverEncodeObject(self, [aDictionary objectForKey:key], NO) forKey:key];
392 
393  [_plistObject setObject:references forKey:aKey];
394 }
395 
396 // Managing classes and class names
404 + (void)setClassName:(CPString)aClassName forClass:(Class)aClass
405 {
408 
409  [CPArchiverReplacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)];
410 }
411 
419 + (CPString)classNameForClass:(Class)aClass
420 {
422  return aClass.name;
423 
424  var className = [CPArchiverReplacementClassNames objectForKey:CPStringFromClass(aClass)];
425 
426  return className ? className : aClass.name;
427 }
428 
436 - (void)setClassName:(CPString)aClassName forClass:(Class)aClass
437 {
438  if (!_replacementClassNames)
439  _replacementClassNames = @{};
440 
441  [_replacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)];
442 }
443 
449 - (CPString)classNameForClass:(Class)aClass
450 {
451  if (!_replacementClassNames)
452  return aClass.name;
453 
454  var className = [_replacementClassNames objectForKey:CPStringFromClass(aClass)];
455 
456  return className ? className : aClass.name;
457 }
458 
459 @end
460 
461 var _CPKeyedArchiverEncodeObject = function(self, anObject, isConditional)
462 {
463  // We wrap primitive JavaScript objects in a unique subclass of CPValue.
464  // This way, when we unarchive, we know to unwrap it, since
465  // _CPKeyedArchiverValue should not be used anywhere else.
466  if (anObject !== nil && anObject !== undefined && !anObject.isa)
467  anObject = [_CPKeyedArchiverValue valueWithJSObject:anObject];
468 
469  // Get the proper replacement object
470  var GUID = [anObject UID],
471  object = [self._replacementObjects objectForKey:GUID];
472 
473  // If a replacement object doesn't exist, then actually ask for one.
474  // Explicitly compare to nil because object could be === 0.
475  if (object === nil)
476  {
477  object = [anObject replacementObjectForKeyedArchiver:self];
478 
479  // Notify our delegate of this replacement.
480  if (self._delegate)
481  {
482  if (object !== anObject && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector)
483  [self._delegate archiver:self willReplaceObject:anObject withObject:object];
484 
485  if (self._delegateSelectors & _CPKeyedArchiverWillEncodeObjectSelector)
486  {
487  anObject = [self._delegate archiver:self willEncodeObject:object];
488 
489  if (anObject !== object && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector)
490  [self._delegate archiver:self willReplaceObject:object withObject:anObject];
491 
492  object = anObject;
493  }
494  }
495 
496  if (object != nil && GUID != nil)
497  [self._replacementObjects setObject:object forKey:GUID];
498  }
499 
500  // If we still don't have an object by this point, then return a
501  // reference to the null object.
502  // Explicitly compare to nil because object could be === 0.
503  if (object === nil)
504  return _CPKeyedArchiverNullReference;
505 
506  // If not, then grab the object's UID
507  var UID = [self._UIDs objectForKey:GUID = [object UID]];
508 
509  // If this object doesn't have a unique index in the object table yet,
510  // then it also hasn't been properly encoded. We explicitly compare
511  // index to nil since it could be 0, which would also evaluate to false.
512  if (UID === nil)
513  {
514  // If it is being conditionally encoded, then
515  if (isConditional)
516  {
517  // If we haven't already noted this conditional object...
518  if ((UID = [self._conditionalUIDs objectForKey:GUID]) === nil)
519  {
520  // Use the null object as a placeholder.
521  [self._conditionalUIDs setObject:UID = [self._plistObjects count] forKey:GUID];
522  [self._plistObjects addObject:_CPKeyedArchiverNullString];
523  }
524  }
525  else
526  {
527  var theClass = [object classForKeyedArchiver],
528  plistObject = nil;
529 
530  if ((theClass === _CPKeyedArchiverStringClass) || (theClass === _CPKeyedArchiverNumberClass))// || theClass == _CPKeyedArchiverBooleanClass)
531  plistObject = object;
532  else
533  {
534  // Only actually encode the object and create a plist representation if it is not a simple type.
535  plistObject = @{};
536 
537  [self._objects addObject:object];
538 
539  var className = [self classNameForClass:theClass];
540 
541  if (!className)
542  className = [[self class] classNameForClass:theClass];
543 
544  if (!className)
545  className = theClass.name;
546  else
547  theClass = CPClassFromString(className);
548 
549  var classUID = [self._UIDs objectForKey:className];
550 
551  if (!classUID)
552  {
553  var plistClass = @{},
554  hierarchy = [];
555 
556  [plistClass setObject:className forKey:_CPKeyedArchiverClassNameKey];
557 
558  do
559  {
560  [hierarchy addObject:CPStringFromClass(theClass)];
561  } while (theClass = [theClass superclass]);
562 
563  [plistClass setObject:hierarchy forKey:_CPKeyedArchiverClassesKey];
564 
565  classUID = [self._plistObjects count];
566  [self._plistObjects addObject:plistClass];
567  [self._UIDs setObject:classUID forKey:className];
568  }
569 
570  [plistObject setObject:@{ _CPKeyedArchiverUIDKey: classUID } forKey:_CPKeyedArchiverClassKey];
571  }
572 
573  UID = [self._conditionalUIDs objectForKey:GUID];
574 
575  // If this object WAS previously encoded conditionally...
576  if (UID !== nil)
577  {
578  [self._UIDs setObject:UID forKey:GUID];
579  [self._plistObjects replaceObjectAtIndex:UID withObject:plistObject];
580  }
581  else
582  {
583  [self._UIDs setObject:UID = [self._plistObjects count] forKey:GUID];
584  [self._plistObjects addObject:plistObject];
585  }
586  }
587  }
588 
589  return @{ _CPKeyedArchiverUIDKey: UID };
590 };
CPString className()
Definition: CPObject.j:527
id initForWritingWithMutableData:(CPMutableData data)
id delegate()
Definition: CALayer.j:965
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
A mutable key-value pair collection.
Definition: CPDictionary.h:2
A generic "value". Can be subclassed to hold specific data types.
Definition: CPValue.h:2
Implements keyed archiving of object graphs (e.g. for storing data).
An immutable string (collection of characters).
Definition: CPString.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
id init()
Definition: CPObject.j:145
CPString classNameForClass:(Class aClass)
var CPArchiverReplacementClassNames
Class class()
Definition: CPObject.j:179
A bridged object to native Javascript numbers.
Definition: CPNumber.h:2
CPData dataWithPlistObject:(id aPlistObject)
Definition: CPData.j:50
function CPClassFromString(aClassName)
Definition: CPObjJRuntime.j:33
id alloc()
Definition: CPObject.j:130