API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
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  _CPKeyedArchiverDidFinishSelector = 8,
30  _CPKeyedArchiverWillFinishSelector = 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 
96 @implementation CPKeyedArchiver : CPCoder
97 {
98  id _delegate;
99  unsigned _delegateSelectors;
100 
101  CPData _data;
102 
103  CPArray _objects;
104 
105  CPDictionary _UIDs;
106  CPDictionary _conditionalUIDs;
107 
108  CPDictionary _replacementObjects;
109  CPDictionary _replacementClassNames;
110 
111  id _plistObject;
112  CPMutableArray _plistObjects;
113 
114  CPPropertyListFormat _outputFormat;
115 }
116 
117 /*
118  @ignore
119 */
120 + (void)initialize
121 {
122  if (self !== [CPKeyedArchiver class])
123  return;
124 
125  _CPKeyedArchiverStringClass = [CPString class];
126  _CPKeyedArchiverNumberClass = [CPNumber class];
127 
128  _CPKeyedArchiverNullReference = [CPDictionary dictionaryWithObject:0 forKey:_CPKeyedArchiverUIDKey];
129 }
130 
131 + (BOOL)allowsKeyedCoding
132 {
133  return YES;
134 }
135 
141 + (CPData)archivedDataWithRootObject:(id)anObject
142 {
143  var data = [CPData dataWithPlistObject:nil],
144  archiver = [[self alloc] initForWritingWithMutableData:data];
145 
146  [archiver encodeObject:anObject forKey:@"root"];
147  [archiver finishEncoding];
148 
149  return data;
150 }
151 
152 // Initializing a CPKeyedArchiver object
158 - (id)initForWritingWithMutableData:(CPMutableData)data
159 {
160  self = [super init];
161 
162  if (self)
163  {
164  _data = data;
165 
166  _objects = [];
167 
168  _UIDs = [CPDictionary dictionary];
169  _conditionalUIDs = [CPDictionary dictionary];
170 
171  _replacementObjects = [CPDictionary dictionary];
172 
173  _plistObject = [CPDictionary dictionary];
174  _plistObjects = [CPArray arrayWithObject:_CPKeyedArchiverNullString];
175  }
176 
177  return self;
178 }
179 
180 // Archiving Data
185 - (void)finishEncoding
186 {
187  if (_delegate && _delegateSelectors & _CPKeyedArchiverWillFinishSelector)
188  [_delegate archiverWillFinish:self];
189 
190  var i = 0,
191  topObject = _plistObject,
192  classes = [];
193 
194  for (; i < _objects.length; ++i)
195  {
196  var object = _objects[i];
197 
198  // Do whatever with the class, yo.
199  // We called willEncodeObject previously.
200 
201  _plistObject = _plistObjects[[_UIDs objectForKey:[object UID]]];
202  [object encodeWithCoder:self];
203 
204  if (_delegate && _delegateSelectors & _CPKeyedArchiverDidEncodeObjectSelector)
205  [_delegate archiver:self didEncodeObject:object];
206  }
207 
208  _plistObject = [CPDictionary dictionary];
209 
210  [_plistObject setObject:topObject forKey:_CPKeyedArchiverTopKey];
211  [_plistObject setObject:_plistObjects forKey:_CPKeyedArchiverObjectsKey];
212  [_plistObject setObject:[self className] forKey:_CPKeyedArchiverArchiverKey];
213  [_plistObject setObject:@"100000" forKey:_CPKeyedArchiverVersionKey];
214 
215  [_data setPlistObject:_plistObject];
216 
217  if (_delegate && _delegateSelectors & _CPKeyedArchiverDidFinishSelector)
218  [_delegate archiverDidFinish:self];
219 }
220 
224 - (CPPropertyListFormat)outputFormat
225 {
226  return _outputFormat;
227 }
228 
233 - (void)setOutputFormat:(CPPropertyListFormat)aPropertyListFormat
234 {
235  _outputFormat = aPropertyListFormat;
236 }
237 
243 - (void)encodeBool:(BOOL)aBOOL forKey:(CPString)aKey
244 {
245  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aBOOL, NO) forKey:aKey];
246 }
247 
253 - (void)encodeDouble:(double)aDouble forKey:(CPString)aKey
254 {
255  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aDouble, NO) forKey:aKey];
256 }
257 
263 - (void)encodeFloat:(float)aFloat forKey:(CPString)aKey
264 {
265  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aFloat, NO) forKey:aKey];
266 }
267 
273 - (void)encodeInt:(float)anInt forKey:(CPString)aKey
274 {
275  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anInt, NO) forKey:aKey];
276 }
277 
278 // Managing Delegates
282 - (void)setDelegate:(id)aDelegate
283 {
284  _delegate = aDelegate;
285 
286  if ([_delegate respondsToSelector:@selector(archiver:didEncodeObject:)])
287  _delegateSelectors |= _CPKeyedArchiverDidEncodeObjectSelector;
288 
289  if ([_delegate respondsToSelector:@selector(archiver:willEncodeObject:)])
290  _delegateSelectors |= _CPKeyedArchiverWillEncodeObjectSelector;
291 
292  if ([_delegate respondsToSelector:@selector(archiver:willReplaceObject:withObject:)])
293  _delegateSelectors |= _CPKeyedArchiverWillReplaceObjectWithObjectSelector;
294 
295  if ([_delegate respondsToSelector:@selector(archiver:didFinishEncoding:)])
296  _delegateSelectors |= _CPKeyedArchiverDidFinishEncodingSelector;
297 
298  if ([_delegate respondsToSelector:@selector(archiver:willFinishEncoding:)])
299  _delegateSelectors |= _CPKeyedArchiverWillFinishEncodingSelector;
300 
301 }
302 
306 - (id)delegate
307 {
308  return _delegate;
309 }
310 
316 - (void)encodePoint:(CGPoint)aPoint forKey:(CPString)aKey
317 {
318  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromPoint(aPoint), NO) forKey:aKey];
319 }
320 
326 - (void)encodeRect:(CGRect)aRect forKey:(CPString)aKey
327 {
328  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromRect(aRect), NO) forKey:aKey];
329 }
330 
336 - (void)encodeSize:(CGSize)aSize forKey:(CPString)aKey
337 {
338  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, CPStringFromSize(aSize), NO) forKey:aKey];
339 }
340 
348 - (void)encodeConditionalObject:(id)anObject forKey:(CPString)aKey
349 {
350  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, YES) forKey:aKey];
351 }
352 
358 - (void)encodeNumber:(CPNumber)aNumber forKey:(CPString)aKey
359 {
360  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, aNumber, NO) forKey:aKey];
361 }
362 
368 - (void)encodeObject:(id)anObject forKey:(CPString)aKey
369 {
370  [_plistObject setObject:_CPKeyedArchiverEncodeObject(self, anObject, NO) forKey:aKey];
371 }
372 
373 /* @ignore */
374 - (void)_encodeArrayOfObjects:(CPArray)objects forKey:(CPString)aKey
375 {
376  var i = 0,
377  count = objects.length,
378  references = [];
379 
380  for (; i < count; ++i)
381  [references addObject:_CPKeyedArchiverEncodeObject(self, objects[i], NO)];
382 
383  [_plistObject setObject:references forKey:aKey];
384 }
385 
386 /* @ignore */
387 - (void)_encodeDictionaryOfObjects:(CPDictionary)aDictionary forKey:(CPString)aKey
388 {
389  var key,
390  keys = [aDictionary keyEnumerator],
391  references = [CPDictionary dictionary];
392 
393  while ((key = [keys nextObject]) !== nil)
394  [references setObject:_CPKeyedArchiverEncodeObject(self, [aDictionary objectForKey:key], NO) forKey:key];
395 
396  [_plistObject setObject:references forKey:aKey];
397 }
398 
399 // Managing classes and class names
407 + (void)setClassName:(CPString)aClassName forClass:(Class)aClass
408 {
411 
412  [CPArchiverReplacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)];
413 }
414 
422 + (CPString)classNameForClass:(Class)aClass
423 {
425  return aClass.name;
426 
427  var className = [CPArchiverReplacementClassNames objectForKey:CPStringFromClass(aClass)];
428 
429  return className ? className : aClass.name;
430 }
431 
439 - (void)setClassName:(CPString)aClassName forClass:(Class)aClass
440 {
441  if (!_replacementClassNames)
442  _replacementClassNames = [CPDictionary dictionary];
443 
444  [_replacementClassNames setObject:aClassName forKey:CPStringFromClass(aClass)];
445 }
446 
452 - (CPString)classNameForClass:(Class)aClass
453 {
454  if (!_replacementClassNames)
455  return aClass.name;
456 
457  var className = [_replacementClassNames objectForKey:CPStringFromClass(aClassName)];
458 
459  return className ? className : aClass.name;
460 }
461 
462 @end
463 
464 var _CPKeyedArchiverEncodeObject = function(self, anObject, isConditional)
465 {
466  // We wrap primitive JavaScript objects in a unique subclass of CPValue.
467  // This way, when we unarchive, we know to unwrap it, since
468  // _CPKeyedArchiverValue should not be used anywhere else.
469  if (anObject !== nil && anObject !== undefined && !anObject.isa)
470  anObject = [_CPKeyedArchiverValue valueWithJSObject:anObject];
471 
472  // Get the proper replacement object
473  var GUID = [anObject UID],
474  object = [self._replacementObjects objectForKey:GUID];
475 
476  // If a replacement object doesn't exist, then actually ask for one.
477  // Explicitly compare to nil because object could be === 0.
478  if (object === nil)
479  {
480  object = [anObject replacementObjectForKeyedArchiver:self];
481 
482  // Notify our delegate of this replacement.
483  if (self._delegate)
484  {
485  if (object !== anObject && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector)
486  [self._delegate archiver:self willReplaceObject:anObject withObject:object];
487 
488  if (self._delegateSelectors & _CPKeyedArchiverWillEncodeObjectSelector)
489  {
490  anObject = [self._delegate archiver:self willEncodeObject:object];
491 
492  if (anObject !== object && self._delegateSelectors & _CPKeyedArchiverWillReplaceObjectWithObjectSelector)
493  [self._delegate archiver:self willReplaceObject:object withObject:anObject];
494 
495  object = anObject;
496  }
497  }
498 
499  [self._replacementObjects setObject:object forKey:GUID];
500  }
501 
502  // If we still don't have an object by this point, then return a
503  // reference to the null object.
504  // Explicitly compare to nil because object could be === 0.
505  if (object === nil)
506  return _CPKeyedArchiverNullReference;
507 
508  // If not, then grab the object's UID
509  var UID = [self._UIDs objectForKey:GUID = [object UID]];
510 
511  // If this object doesn't have a unique index in the object table yet,
512  // then it also hasn't been properly encoded. We explicitly compare
513  // index to nil since it could be 0, which would also evaluate to false.
514  if (UID === nil)
515  {
516  // If it is being conditionally encoded, then
517  if (isConditional)
518  {
519  // If we haven't already noted this conditional object...
520  if ((UID = [self._conditionalUIDs objectForKey:GUID]) === nil)
521  {
522  // Use the null object as a placeholder.
523  [self._conditionalUIDs setObject:UID = [self._plistObjects count] forKey:GUID];
524  [self._plistObjects addObject:_CPKeyedArchiverNullString];
525  }
526  }
527  else
528  {
529  var theClass = [object classForKeyedArchiver],
530  plistObject = nil;
531 
532  if ((theClass === _CPKeyedArchiverStringClass) || (theClass === _CPKeyedArchiverNumberClass))// || theClass == _CPKeyedArchiverBooleanClass)
533  plistObject = object;
534  else
535  {
536  // Only actually encode the object and create a plist representation if it is not a simple type.
537  plistObject = [CPDictionary dictionary];
538 
539  [self._objects addObject:object];
540 
541  var className = [self classNameForClass:theClass];
542 
543  if (!className)
544  className = [[self class] classNameForClass:theClass];
545 
546  if (!className)
547  className = theClass.name;
548  else
549  theClass = CPClassFromString(className);
550 
551  var classUID = [self._UIDs objectForKey:className];
552 
553  if (!classUID)
554  {
555  var plistClass = [CPDictionary dictionary],
556  hierarchy = [];
557 
558  [plistClass setObject:className forKey:_CPKeyedArchiverClassNameKey];
559 
560  do
561  {
562  [hierarchy addObject:CPStringFromClass(theClass)];
563  } while (theClass = [theClass superclass]);
564 
565  [plistClass setObject:hierarchy forKey:_CPKeyedArchiverClassesKey];
566 
567  classUID = [self._plistObjects count];
568  [self._plistObjects addObject:plistClass];
569  [self._UIDs setObject:classUID forKey:className];
570  }
571 
572  [plistObject setObject:[CPDictionary dictionaryWithObject:classUID forKey:_CPKeyedArchiverUIDKey] forKey:_CPKeyedArchiverClassKey];
573  }
574 
575  UID = [self._conditionalUIDs objectForKey:GUID];
576 
577  // If this object WAS previously encoded conditionally...
578  if (UID !== nil)
579  {
580  [self._UIDs setObject:UID forKey:GUID];
581  [self._plistObjects replaceObjectAtIndex:UID withObject:plistObject];
582  }
583  else
584  {
585  [self._UIDs setObject:UID = [self._plistObjects count] forKey:GUID];
586  [self._plistObjects addObject:plistObject];
587  }
588  }
589  }
590 
591  return [CPDictionary dictionaryWithObject:UID forKey:_CPKeyedArchiverUIDKey];
592 };