API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPDictionary.j
Go to the documentation of this file.
1 /*
2  * CPDictionary.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 //FIXME: After release of 0.9.7 remove below variable
26 
28 
29 /* @ignore */
30 @implementation _CPDictionaryValueEnumerator : CPEnumerator
31 {
32  CPEnumerator _keyEnumerator;
33  CPDictionary _dictionary;
34 }
35 
36 - (id)initWithDictionary:(CPDictionary)aDictionary
37 {
38  self = [super init];
39 
40  if (self)
41  {
42  _keyEnumerator = [aDictionary keyEnumerator];
43  _dictionary = aDictionary;
44  }
45 
46  return self;
47 }
48 
49 - (id)nextObject
50 {
51  var key = [_keyEnumerator nextObject];
52 
53  if (key === nil)
54  return nil;
55 
56  return [_dictionary objectForKey:key];
57 }
58 
59 @end
60 
78 @implementation CPDictionary : CPObject
79 {
80  id __doxygen__;
81 }
82 
83 /*
84  @ignore
85 */
86 + (id)alloc
87 {
88  var result = new CFMutableDictionary();
89  result.isa = [self class];
90  return result;
91 }
92 
96 + (id)dictionary
97 {
98  return [[self alloc] init];
99 }
100 
106 + (id)dictionaryWithDictionary:(CPDictionary)aDictionary
107 {
108  return [[self alloc] initWithDictionary:aDictionary];
109 }
110 
117 + (id)dictionaryWithObject:(id)anObject forKey:(id)aKey
118 {
119  return [[self alloc] initWithObjects:[anObject] forKeys:[aKey]];
120 }
121 
129 + (id)dictionaryWithObjects:(CPArray)objects forKeys:(CPArray)keys
130 {
131  return [[self alloc] initWithObjects:objects forKeys:keys];
132 }
133 
139 + (id)dictionaryWithJSObject:(JSObject)object
140 {
141  return [self dictionaryWithJSObject:object recursively:NO];
142 }
143 
149 + (id)dictionaryWithJSObject:(JSObject)object recursively:(BOOL)recursively
150 {
151  var key = "",
152  dictionary = [[self alloc] init];
153 
154  for (key in object)
155  {
156  if (!object.hasOwnProperty(key))
157  continue;
158 
159  var value = object[key];
160 
161  if (value === null)
162  {
163  [dictionary setObject:[CPNull null] forKey:key];
164  continue;
165  }
166 
167  if (recursively)
168  {
169  if (value.constructor === Object)
170  value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
171  else if ([value isKindOfClass:CPArray])
172  {
173  var newValue = [],
174  i = 0,
175  count = value.length;
176 
177  for (; i < count; i++)
178  {
179  var thisValue = value[i];
180 
181  if (thisValue === null)
182  {
183  newValue.push([CPNull null]);
184  }
185  else
186  {
187  if (thisValue.constructor === Object)
188  newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
189  else
190  newValue.push(thisValue);
191  }
192  }
193 
194  value = newValue;
195  }
196  }
197 
198  [dictionary setObject:value forKey:key];
199  }
200 
201  return dictionary;
202 }
203 
221 + (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...
222 {
223  arguments[0] = [self alloc];
224  arguments[1] = @selector(initWithObjectsAndKeys:);
225 
226  return objj_msgSend.apply(this, arguments);
227 }
228 
234 - (id)initWithDictionary:(CPDictionary)aDictionary
235 {
236  var key = "",
237  dictionary = [[CPDictionary alloc] init];
238 
239  for (key in aDictionary._buckets)
240  [dictionary setObject:[aDictionary objectForKey:key] forKey:key];
241 
242  return dictionary;
243 }
244 
252 - (id)initWithObjects:(CPArray)objects forKeys:(CPArray)keyArray
253 {
254  self = [super init];
255 
256  if ([objects count] != [keyArray count])
257  [CPException raise:CPInvalidArgumentException reason:[CPString stringWithFormat:@"Counts are different.(%d != %d)", [objects count], [keyArray count]]];
258 
259  if (self)
260  {
261  var i = [keyArray count];
262 
263  while (i--)
264  {
265  var value = objects[i],
266  key = keyArray[i];
267 
268  if (value === nil)
269  {
271  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: Attempt to insert nil object from objects[%d]", [self className], _cmd, i]);
272 
273  if (typeof(objj_backtrace_print) === "function")
274  objj_backtrace_print(CPLog.warn);
275 
276  // FIXME: After release of 0.9.7 change this block to:
277  // [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + i + @"]"];
278  }
279 
280  if (key === nil)
281  {
283  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: Attempt to insert nil key from keys[%d]", [self className], _cmd, i]);
284 
285  if (typeof(objj_backtrace_print) === "function")
286  objj_backtrace_print(CPLog.warn);
287 
288  // FIXME: After release of 0.9.7 change this block to:
289  // [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + i + @"]"];
290  }
291 
292  [self setObject:value forKey:key];
293  }
294  }
295 
296  return self;
297 }
298 
313 - (id)initWithObjectsAndKeys:(id)firstObject, ...
314 {
315  var argCount = arguments.length;
316 
317  if (argCount % 2 !== 0)
318  [CPException raise:CPInvalidArgumentException reason:"Key-value count is mismatched. (" + argCount + " arguments passed)"];
319 
320  self = [super init];
321 
322  if (self)
323  {
324  // The arguments array contains self and _cmd, so the first object is at position 2.
325  var index = 2;
326 
327  for (; index < argCount; index += 2)
328  {
329  var value = arguments[index],
330  key = arguments[index + 1];
331 
332  if (value === nil)
333  {
335  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: Attempt to insert nil object from objects[%d]", [self className], _cmd, (index / 2) - 1]);
336 
337  if (typeof(objj_backtrace_print) === "function")
338  objj_backtrace_print(CPLog.warn);
339 
340  // FIXME: After release of 0.9.7 change 3 lines above to this:
341  // [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((index / 2) - 1) + @"]"];
342  }
343 
344  if (key === nil)
345  {
347  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: Attempt to insert nil key from keys[%d]", [self className], _cmd, (index / 2) - 1]);
348 
349  if (typeof(objj_backtrace_print) === "function")
350  objj_backtrace_print(CPLog.warn);
351 
352  // FIXME: After release of 0.9.7 change 3 lines above to this:
353  // [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((index / 2) - 1) + @"]"];
354  }
355 
356  [self setObject:value forKey:key];
357  }
358  }
359 
360  return self;
361 }
362 
367 {
369 }
370 
374 - (int)count
375 {
376  return self._count;
377 }
378 
382 - (CPArray)allKeys
383 {
384  return [self._keys copy];
385 }
386 
390 - (CPArray)allValues
391 {
392  var keys = self._keys,
393  index = keys.length,
394  values = [];
395 
396  while (index--)
397  values.push(self.valueForKey(keys[index]));
398 
399  return values;
400 }
401 
410 - (CPArray)allKeysForObject:(id)anObject
411 {
412  var keys = self._keys,
413  count = keys.length,
414  index = 0,
415  matchingKeys = [],
416  key = nil,
417  value = nil;
418 
419  for (; index < count; ++index)
420  {
421  key = keys[index];
422  value = self._buckets[key];
423 
424  if (value.isa && anObject && anObject.isa && [value respondsToSelector:@selector(isEqual:)] && [value isEqual:anObject])
425  matchingKeys.push(key);
426  else if (value === anObject)
427  matchingKeys.push(key);
428  }
429 
430  return matchingKeys;
431 }
432 
433 - (CPArray)keysOfEntriesPassingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
434 {
435  return [self keysOfEntriesWithOptions:CPEnumerationNormal passingTest:predicate];
436 }
437 
438 - (CPArray)keysOfEntriesWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
439 {
440  var keys = self._keys;
441 
442  if (options & CPEnumerationReverse)
443  {
444  var index = [keys count] - 1,
445  stop = -1,
446  increment = -1;
447  }
448  else
449  {
450  var index = 0,
451  stop = [keys count],
452  increment = 1;
453  }
454 
455  var matchingKeys = [],
456  key = nil,
457  value = nil,
458  shouldStop = NO,
459  stopRef = @ref(shouldStop);
460 
461  for (; index !== stop; index += increment)
462  {
463  key = keys[index];
464  value = self._buckets[key];
465 
466  if (predicate(key, value, stopRef))
467  matchingKeys.push(key);
468 
469  if (shouldStop)
470  break;
471  }
472 
473  return matchingKeys;
474 }
475 
476 - (CPArray)keysSortedByValueUsingComparator:(Function /*(id obj1, id obj2)*/)comparator
477 {
478  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
479  {
480  a = [self objectForKey:a];
481  b = [self objectForKey:b];
482 
483  return comparator(a, b);
484  }
485  ];
486 }
487 
488 - (CPArray)keysSortedByValueUsingSelector:(SEL)theSelector
489 {
490  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
491  {
492  a = [self objectForKey:a];
493  b = [self objectForKey:b];
494 
495  return [a performSelector:theSelector withObject:b];
496  }
497  ];
498 }
499 
503 - (CPEnumerator)keyEnumerator
504 {
505  return [self._keys objectEnumerator];
506 }
507 
511 - (CPEnumerator)objectEnumerator
512 {
513  return [[_CPDictionaryValueEnumerator alloc] initWithDictionary:self];
514 }
515 
519 - (BOOL)isEqualToDictionary:(CPDictionary)aDictionary
520 {
521  if (self === aDictionary)
522  return YES;
523 
524  var count = [self count];
525 
526  if (count !== [aDictionary count])
527  return NO;
528 
529  var index = count,
530  keys = self._keys;
531 
532  while (index--)
533  {
534  var currentKey = keys[index],
535  lhsObject = self._buckets[currentKey],
536  rhsObject = aDictionary._buckets[currentKey];
537 
538  if (lhsObject === rhsObject)
539  continue;
540 
541  if (lhsObject && lhsObject.isa && rhsObject && rhsObject.isa && [lhsObject respondsToSelector:@selector(isEqual:)] && [lhsObject isEqual:rhsObject])
542  continue;
543 
544  return NO;
545  }
546 
547  return YES;
548 }
549 
550 - (BOOL)isEqual:(id)anObject
551 {
552  if (self === anObject)
553  return YES;
554 
555  if (![anObject isKindOfClass:[CPDictionary class]])
556  return NO;
557 
558  return [self isEqualToDictionary:anObject];
559 }
560 
561 /*
562  Instance.allKeysForObject(anObject)
563  {
564  var i= 0,
565  keys= CPArray.array(),
566  count= this.count();
567 
568  while ((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
569 
570  return keys;
571  }
572 
573  Instance.keysSortedByValueUsingSelector(aSelector)
574  {
575  var dictionary= this,
576  objectSelector= function(rhs)
577  {
578  return aSelector.apply(dictionary.objectForKey(this), [dictionary.objectForKey(rhs)]);
579  };
580 
581  return this._keys.sortedArrayUsingSelector(objectSelector);
582  }
583 */
589 - (id)objectForKey:(id)aKey
590 {
591  var object = self._buckets[aKey];
592 
593  return (object === undefined) ? nil : object;
594 }
595 /*
596  Instance.objectsForKeys(keys, aNotFoundMarker)
597  {
598  var i= keys.length,
599  objects= CPArray.array();
600 
601  while (i--)
602  {
603  var object= this.objectForKey(keys[i]);
604  objects.addObject(object==nil?aNotFoundMarker:object);
605  }
606 
607  return objects;
608  }
609 
610  Instance.valueForKey(aKey)
611  {
612  if (aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
613 
614  return base.valueForKey(aKey);
615  }
616 */
620 - (void)removeAllObjects
621 {
622  self.removeAllValues();
623 }
624 
629 - (void)removeObjectForKey:(id)aKey
630 {
631  self.removeValueForKey(aKey);
632 }
633 
638 - (void)removeObjectsForKeys:(CPArray)keysForRemoval
639 {
640  var index = keysForRemoval.length;
641 
642  while (index--)
643  [self removeObjectForKey:keysForRemoval[index]];
644 }
645 
646 /*
647  Instance.setDictionary(aDictionary)
648  {
649  this._keys= CPArray.arrayWithArray(aDictionary.allKeys());
650  this._objects= CPArray.arrayWithArray(aDictionary.allValues());
651 
652  this._dictionary= { };
653 
654  var i= this._keys.count();
655  while (i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
656  }
657 */
663 - (void)setObject:(id)anObject forKey:(id)aKey
664 {
665  // FIXME: After release of 0.9.7, remove this test and leave the contents of its block
667  {
668  if (aKey === nil)
669  {
670  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: key cannot be nil", [self className], _cmd]);
671 
672  if (typeof(objj_backtrace_print) === "function")
673  objj_backtrace_print(CPLog.warn);
674 
675  // FIXME: After release of 0.9.7 change this block to:
676  // [CPException raise:CPInvalidArgumentException reason:@"key cannot be nil"];
677  }
678 
679  if (anObject === nil)
680  {
681  CPLog.warn([CPString stringWithFormat:@"[%s %s] DEPRECATED: object cannot be nil (key: %s)", [self className], _cmd, aKey]);
682 
683  if (typeof(objj_backtrace_print) === "function")
684  objj_backtrace_print(CPLog.warn);
685 
686  // FIXME: After release of 0.9.7 change this block to:
687  // [CPException raise:CPInvalidArgumentException reason:@"object cannot be nil (key: " + aKey + @")"];
688  }
689  }
690  // FIXME: After release of 0.9.7 remove 2 lines below.
691  else
693 
694  self.setValueForKey(aKey, anObject);
695 }
696 
700 - (void)addEntriesFromDictionary:(CPDictionary)aDictionary
701 {
702  if (!aDictionary)
703  return;
704 
705  var keys = [aDictionary allKeys],
706  index = [keys count];
707 
708  while (index--)
709  {
710  var key = keys[index];
711 
712  [self setObject:[aDictionary objectForKey:key] forKey:key];
713  }
714 }
715 
719 - (CPString)description
720 {
721  var string = "@{",
722  keys = [self allKeys],
723  index = 0,
724  count = self._count;
725 
726  keys.sort();
727 
728  for (; index < count; ++index)
729  {
730  if (index === 0)
731  string += "\n";
732 
733  var key = keys[index],
734  value = self.valueForKey(key);
735 
736  string += " @\"" + key + "\": " + CPDescriptionOfObject(value, CPDictionaryMaxDescriptionRecursion).split("\n").join("\n ") + (index + 1 < count ? "," : "") + "\n";
737  }
738 
739  return string + "}";
740 }
741 
742 - (BOOL)containsKey:(id)aKey
743 {
744  var value = [self objectForKey:aKey];
745  return ((value !== nil) && (value !== undefined));
746 }
747 
748 - (void)enumerateKeysAndObjectsUsingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
749 {
750  var shouldStop = NO,
751  shouldStopRef = @ref(shouldStop),
752  keys = self._keys,
753  count = self._count;
754 
755  for (var index = 0; index < count; index++)
756  {
757  var key = keys[index],
758  value = self.valueForKey(key);
759 
760  aFunction(key, value, shouldStopRef);
761 
762  if (shouldStop)
763  return;
764  }
765 }
766 
767 - (void)enumerateKeysAndObjectsWithOptions:(CPEnumerationOptions)opts usingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
768 {
769  // Ignore the options because neither option has an effect.
770  // CPEnumerationReverse has no effect on enumerating a CPDictionary because dictionary enumeration is not ordered.
771  // CPEnumerationConcurrent is not possible in a single threaded environment.
772  [self enumerateKeysAndObjectsUsingBlock:aFunction];
773 }
774 
775 @end
776 
777 @implementation CPDictionary (CPCoding)
778 
779 /*
780  Initializes the dictionary by unarchiving the data from a coder.
781  @param aCoder the coder from which the data will be unarchived.
782  @return the initialized dictionary
783 */
784 - (id)initWithCoder:(CPCoder)aCoder
785 {
786  return [aCoder _decodeDictionaryOfObjectsForKey:@"CP.objects"];
787 }
788 
793 - (void)encodeWithCoder:(CPCoder)aCoder
794 {
795  [aCoder _encodeDictionaryOfObjects:self forKey:@"CP.objects"];
796 }
797 
798 @end
799 
809 {
810  id __doxygen__;
811 }
812 
813 @end
814 
815 CFDictionary.prototype.isa = CPDictionary;
816 CFMutableDictionary.prototype.isa = CPMutableDictionary;