API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPKeyedUnarchiver.j
Go to the documentation of this file.
1 /*
2  * CPKeyedUnarchiver.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 
24 CPInvalidUnarchiveOperationException = @"CPInvalidUnarchiveOperationException";
25 
26 var _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector = 1 << 0,
27  _CPKeyedUnarchiverDidDecodeObjectSelector = 1 << 1,
28  _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector = 1 << 2,
29  _CPKeyedUnarchiverWillFinishSelector = 1 << 3,
30  _CPKeyedUnarchiverDidFinishSelector = 1 << 4,
32 
33 var _CPKeyedArchiverNullString = "$null",
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 CPArrayClass = Nil,
52  CPDataClass = Nil,
53  _CPKeyedArchiverValueClass = Nil;
54 
101 @implementation CPKeyedUnarchiver : CPCoder
102 {
103  id _delegate;
104  unsigned _delegateSelectors;
105 
106  CPData _data;
107 
108  CPDictionary _replacementClasses;
109 
110  CPArray _objects;
111  CPDictionary _archive;
112 
113  CPDictionary _plistObject;
114  CPArray _plistObjects;
115 }
116 
117 /*
118  @ignore
119 */
120 + (void)initialize
121 {
122  if (self !== [CPKeyedUnarchiver class])
123  return;
124 
132  _CPKeyedArchiverValueClass = [_CPKeyedArchiverValue class];
133 }
134 
135 /*
136  Initializes the receiver to unarchive objects from the specified data object.
137  @param data the data stream from which to read objects
138  @return the initialized unarchiver
139 */
140 - (id)initForReadingWithData:(CPData)data
141 {
142  self = [super init];
143 
144  if (self)
145  {
146  _archive = [data plistObject];
147  _objects = [[CPNull null]];
148 
149  _plistObject = [_archive objectForKey:_CPKeyedArchiverTopKey];
150  _plistObjects = [_archive objectForKey:_CPKeyedArchiverObjectsKey];
151 
152  _replacementClasses = new CFMutableDictionary();
153  }
154 
155  return self;
156 }
157 
158 /*
159  Unarchives the object graph in the provided data object.
160  @param data the data from which to read the graph
161  @return the unarchived object
162 */
163 + (id)unarchiveObjectWithData:(CPData)aData
164 {
165  if (!aData)
166  {
167  CPLog.error("Null data passed to -[CPKeyedUnarchiver unarchiveObjectWithData:].");
168  return nil;
169  }
170 
171  var unarchiver = [[self alloc] initForReadingWithData:aData],
172  object = [unarchiver decodeObjectForKey:@"root"];
173 
174  [unarchiver finishDecoding];
175 
176  return object;
177 }
178 
179 /*
180  Not implemented
181 */
182 + (id)unarchiveObjectWithFile:(CPString)aFilePath
183 {
184 }
185 
186 /*
187  Not implemented
188 */
189 + (id)unarchiveObjectWithFile:(CPString)aFilePath asynchronously:(BOOL)aFlag
190 {
191 }
192 
193 /*
194  Returns \c YES if an object exists for \c aKey.
195  @param aKey the object's associated key
196 */
197 - (BOOL)containsValueForKey:(CPString)aKey
198 {
199  return _plistObject.valueForKey(aKey) != nil;
200 }
201 
202 /* @ignore */
203 - (CPDictionary)_decodeDictionaryOfObjectsForKey:(CPString)aKey
204 {
205  var object = _plistObject.valueForKey(aKey),
206  objectClass = (object != nil) && object.isa;
207 
208  if (objectClass === CPDictionaryClass || objectClass === CPMutableDictionaryClass)
209  {
210  var keys = object.keys(),
211  index = 0,
212  count = keys.length,
213  dictionary = new CFMutableDictionary();
214 
215  for (; index < count; ++index)
216  {
217  var key = keys[index];
218 
219  dictionary.setValueForKey(key, _CPKeyedUnarchiverDecodeObjectAtIndex(self, object.valueForKey(key).valueForKey(_CPKeyedArchiverUIDKey)));
220  }
221 
222  return dictionary;
223  }
224 
225  return nil;
226 }
227 
228 /*
229  Decodes a \c BOOL from the archive
230  @param aKey the \c BOOL's associated key
231  @return the decoded \c BOOL
232 */
233 - (BOOL)decodeBoolForKey:(CPString)aKey
234 {
235  return !![self decodeObjectForKey:aKey];
236 }
237 
238 /*
239  Decodes a \c float from the archive
240  @param aKey the \c float's associated key
241  @return the decoded \c float
242 */
243 - (float)decodeFloatForKey:(CPString)aKey
244 {
245  var f = [self decodeObjectForKey:aKey];
246 
247  return f === nil ? 0.0 : f;
248 }
249 
250 /*
251  Decodes a \c double from the archive.
252  @param aKey the \c double's associated key
253  @return the decoded \c double
254 */
255 - (double)decodeDoubleForKey:(CPString)aKey
256 {
257  var d = [self decodeObjectForKey:aKey];
258 
259  return d === nil ? 0.0 : d;
260 }
261 
262 /*
263  Decodes an \c int from the archive.
264  @param aKey the \c int's associated key
265  @return the decoded \c int
266 */
267 - (int)decodeIntForKey:(CPString)aKey
268 {
269  var i = [self decodeObjectForKey:aKey];
270 
271  return i === nil ? 0 : i;
272 }
273 
274 /*
275  Decodes a CGPoint from the archive.
276  @param aKey the point's associated key
277  @return the decoded point
278 */
279 - (CGPoint)decodePointForKey:(CPString)aKey
280 {
281  var object = [self decodeObjectForKey:aKey];
282 
283  if (object)
284  return CGPointFromString(object);
285  else
286  return CGPointMakeZero();
287 }
288 
289 /*
290  Decodes a CGRect from the archive.
291  @param aKey the rectangle's associated key
292  @return the decoded rectangle
293 */
294 - (CGRect)decodeRectForKey:(CPString)aKey
295 {
296  var object = [self decodeObjectForKey:aKey];
297 
298  if (object)
299  return CGRectFromString(object);
300  else
301  return CGRectMakeZero();
302 }
303 
304 /*
305  Decodes a CGSize from the archive.
306  @param aKey the size's associated key
307  @return the decoded size
308 */
309 - (CGSize)decodeSizeForKey:(CPString)aKey
310 {
311  var object = [self decodeObjectForKey:aKey];
312 
313  if (object)
314  return CGSizeFromString(object);
315  else
316  return CGSizeMakeZero();
317 }
318 
319 /*
320  Decodes an object from the archive.
321  @param aKey the object's associated key
322  @return the decoded object
323 */
324 - (id)decodeObjectForKey:(CPString)aKey
325 {
326  var object = _plistObject.valueForKey(aKey),
327  objectClass = (object != nil) && object.isa;
328 
329  if (objectClass === CPDictionaryClass || objectClass === CPMutableDictionaryClass)
330  return _CPKeyedUnarchiverDecodeObjectAtIndex(self, object.valueForKey(_CPKeyedArchiverUIDKey));
331 
332  else if (objectClass === CPNumberClass || objectClass === CPDataClass || objectClass === CPStringClass)
333  return object;
334 
335  else if (objectClass === _CPJavaScriptArray)
336  {
337  var index = 0,
338  count = object.length,
339  array = [];
340 
341  for (; index < count; ++index)
342  array[index] = _CPKeyedUnarchiverDecodeObjectAtIndex(self, object[index].valueForKey(_CPKeyedArchiverUIDKey));
343 
344  return array;
345  }
346 /* else
347  CPLog([object className] + " " + object + " " + aKey + " " + [_plistObject description]);*/
348 
349  return nil;
350 }
351 
352 /*
353  Decodes bytes from the archive.
354  @param aKey the object's associated key
355  @return array of bytes
356 */
357 - (id)decodeBytesForKey:(CPString)aKey
358 {
359  // We get the CPData wrapper, then extract the bytes array
360  var data = [self decodeObjectForKey:aKey];
361 
362  if (!data)
363  return nil;
364 
365  var objectClass = data.isa;
366 
367  if (objectClass === CPDataClass)
368  return data.bytes();
369 
370  return nil;
371 }
372 
373 /*
374  Notifies the delegates that decoding has finished.
375 */
376 - (void)finishDecoding
377 {
378  if (_delegateSelectors & _CPKeyedUnarchiverWillFinishSelector)
379  [_delegate unarchiverWillFinish:self];
380 
381  if (_delegateSelectors & _CPKeyedUnarchiverDidFinishSelector)
382  [_delegate unarchiverDidFinish:self];
383 }
384 
385 /*
386  Returns the keyed unarchiver's delegate
387 */
388 - (id)delegate
389 {
390  return _delegate;
391 }
392 
393 /*
394  Sets the unarchiver's delegate
395  @param the new delegate
396 */
397 - (void)setDelegate:(id)aDelegate
398 {
399  _delegate = aDelegate;
400 
401  if ([_delegate respondsToSelector:@selector(unarchiver:cannotDecodeObjectOfClassName:originalClasses:)])
402  _delegateSelectors |= _CPKeyedUnarchiverCannotDecodeObjectOfClassNameOriginalClassesSelector;
403 
404  if ([_delegate respondsToSelector:@selector(unarchiver:didDecodeObject:)])
405  _delegateSelectors |= _CPKeyedUnarchiverDidDecodeObjectSelector;
406 
407  if ([_delegate respondsToSelector:@selector(unarchiver:willReplaceObject:withObject:)])
408  _delegateSelectors |= _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector;
409 
410  if ([_delegate respondsToSelector:@selector(unarchiverWillFinish:)])
411  _delegateSelectors |= _CPKeyedUnarchiverWilFinishSelector;
412 
413  if ([_delegate respondsToSelector:@selector(unarchiverDidFinish:)])
414  _delegateSelectors |= _CPKeyedUnarchiverDidFinishSelector;
415 
416  if ([_delegate respondsToSelector:@selector(unarchiver:cannotDecodeObjectOfClassName:originalClasses:)])
418 }
419 
420 - (void)setClass:(Class)aClass forClassName:(CPString)aClassName
421 {
422  _replacementClasses.setValueForKey(aClassName, aClass);
423 }
424 
425 - (Class)classForClassName:(CPString)aClassName
426 {
427  return _replacementClasses.valueForKey(aClassName);
428 }
429 
430 - (BOOL)allowsKeyedCoding
431 {
432  return YES;
433 }
434 
435 @end
436 
437 var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex)
438 {
439  var object = self._objects[anIndex];
440 
441  if (object)
442  {
443  if (object === self._objects[0])
444  return nil;
445  // Don't return immediately here. The _CPKeyedArchiverValueClass unwrapper code
446  // hasn't executed yet.
447  }
448  else
449  {
450  var plistObject = self._plistObjects[anIndex],
451  plistObjectClass = plistObject.isa;
452 
453  if (plistObjectClass === CPDictionaryClass || plistObjectClass === CPMutableDictionaryClass)
454  {
455  var plistClass = self._plistObjects[plistObject.valueForKey(_CPKeyedArchiverClassKey).valueForKey(_CPKeyedArchiverUIDKey)],
456  className = plistClass.valueForKey(_CPKeyedArchiverClassNameKey),
457  classes = plistClass.valueForKey(_CPKeyedArchiverClassesKey),
458  theClass = [self classForClassName:className];
459 
460  if (!theClass)
461  theClass = CPClassFromString(className);
462 
464  theClass = [_delegate unarchiver:self cannotDecodeObjectOfClassName:className originalClasses:classes];
465 
466  if (!theClass)
467  [CPException raise:CPInvalidUnarchiveOperationException reason:@"-[CPKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (" + className + @")"];
468 
469  var savedPlistObject = self._plistObject;
470 
471  self._plistObject = plistObject;
472 
473  // Should we only call this on _CPCibClassSwapper? (currently the only class that makes use of this).
474  object = [theClass allocWithCoder:self];
475 
476  // It is important to do this before calling initWithCoder so that decoding can be self referential (something = self).
477  self._objects[anIndex] = object;
478 
479  var processedObject = [object initWithCoder:self];
480 
481  self._plistObject = savedPlistObject;
482 
483  if (processedObject !== object)
484  {
485  if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
486  [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
487 
488  object = processedObject;
489  self._objects[anIndex] = processedObject;
490  }
491 
492  processedObject = [object awakeAfterUsingCoder:self];
493 
494  if (processedObject !== object)
495  {
496  if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
497  [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
498 
499  object = processedObject;
500  self._objects[anIndex] = processedObject;
501  }
502 
503  if (self._delegate)
504  {
505  if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector)
506  processedObject = [self._delegate unarchiver:self didDecodeObject:object];
507 
508  if (processedObject && processedObject != object)
509  {
510  if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
511  [self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
512 
513  object = processedObject;
514  self._objects[anIndex] = processedObject;
515  }
516  }
517  }
518  else
519  {
520  self._objects[anIndex] = object = plistObject;
521 
522  if ([object class] === CPStringClass)
523  {
524  if (object === _CPKeyedArchiverNullString)
525  {
526  self._objects[anIndex] = self._objects[0];
527 
528  return nil;
529  }
530  else
531  self._objects[anIndex] = object = plistObject;
532  }
533  }
534  }
535 
536  // If this object is a member of _CPKeyedArchiverValue, then we know
537  // that it is a wrapper for a primitive JavaScript object.
538  if ((object != nil) && (object.isa === _CPKeyedArchiverValueClass))
539  object = [object JSObject];
540 
541  return object;
542 };