API  0.9.8
 All Classes Files Functions Variables Typedefs 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 
25 
42 @implementation CPDictionary : CPObject
43 {
44  id __doxygen__;
45 }
46 
47 /*
48  @ignore
49 */
50 + (id)alloc
51 {
52  var result = new CFMutableDictionary();
53  result.isa = [self class];
54  return result;
55 }
56 
60 + (id)dictionary
61 {
62  return [[self alloc] init];
63 }
64 
70 + (id)dictionaryWithDictionary:(CPDictionary)aDictionary
71 {
72  return [[self alloc] initWithDictionary:aDictionary];
73 }
74 
81 + (id)dictionaryWithObject:(id)anObject forKey:(id)aKey
82 {
83  return [[self alloc] initWithObjects:[anObject] forKeys:[aKey]];
84 }
85 
93 + (id)dictionaryWithObjects:(CPArray)objects forKeys:(CPArray)keys
94 {
95  return [[self alloc] initWithObjects:objects forKeys:keys];
96 }
97 
103 + (id)dictionaryWithJSObject:(JSObject)object
104 {
105  return [self dictionaryWithJSObject:object recursively:NO];
106 }
107 
113 + (id)dictionaryWithJSObject:(JSObject)object recursively:(BOOL)recursively
114 {
115  var key = "",
116  dictionary = [[self alloc] init];
117 
118  for (key in object)
119  {
120  if (!object.hasOwnProperty(key))
121  continue;
122 
123  var value = object[key];
124 
125  if (value === null)
126  {
127  [dictionary setObject:[CPNull null] forKey:key];
128  continue;
129  }
130 
131  if (recursively)
132  {
133  if (value.constructor === Object)
134  value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
135  else if ([value isKindOfClass:CPArray])
136  {
137  var newValue = [],
138  i = 0,
139  count = value.length;
140 
141  for (; i < count; i++)
142  {
143  var thisValue = value[i];
144 
145  if (thisValue === null)
146  {
147  newValue.push([CPNull null]);
148  }
149  else
150  {
151  if (thisValue.constructor === Object)
152  newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
153  else
154  newValue.push(thisValue);
155  }
156  }
157 
158  value = newValue;
159  }
160  }
161 
162  [dictionary setObject:value forKey:key];
163  }
164 
165  return dictionary;
166 }
167 
185 + (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...
186 {
187  arguments[0] = [self alloc];
188  arguments[1] = @selector(initWithObjectsAndKeys:);
189 
190  return objj_msgSend.apply(this, arguments);
191 }
192 
198 - (id)initWithDictionary:(CPDictionary)aDictionary
199 {
200  var key = "",
201  dictionary = [[CPDictionary alloc] init];
202 
203  for (key in aDictionary._buckets)
204  [dictionary setObject:[aDictionary objectForKey:key] forKey:key];
205 
206  return dictionary;
207 }
208 
216 - (id)initWithObjects:(CPArray)objects forKeys:(CPArray)keyArray
217 {
218  self = [super init];
219 
220  if ([objects count] != [keyArray count])
221  [CPException raise:CPInvalidArgumentException reason:[CPString stringWithFormat:@"Counts are different.(%d != %d)", [objects count], [keyArray count]]];
222 
223  if (self)
224  {
225  var i = [keyArray count];
226 
227  while (i--)
228  {
229  var value = objects[i],
230  key = keyArray[i];
231 
232  if (value === nil)
233  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + i + @"]"];
234 
235  if (key === nil)
236  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + i + @"]"];
237 
238  [self setObject:value forKey:key];
239  }
240  }
241 
242  return self;
243 }
244 
259 - (id)initWithObjectsAndKeys:(id)firstObject, ...
260 {
261  var argCount = arguments.length;
262 
263  if (argCount % 2 !== 0)
264  [CPException raise:CPInvalidArgumentException reason:"Key-value count is mismatched. (" + argCount + " arguments passed)"];
265 
266  self = [super init];
267 
268  if (self)
269  {
270  // The arguments array contains self and _cmd, so the first object is at position 2.
271  var index = 2;
272 
273  for (; index < argCount; index += 2)
274  {
275  var value = arguments[index],
276  key = arguments[index + 1];
277 
278  if (value === nil)
279  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((index / 2) - 1) + @"]"];
280 
281  if (key === nil)
282  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((index / 2) - 1) + @"]"];
283 
284  [self setObject:value forKey:key];
285  }
286  }
287 
288  return self;
289 }
290 
295 {
297 }
298 
302 - (int)count
303 {
304  return self._count;
305 }
306 
310 - (CPArray)allKeys
311 {
312  return [self._keys copy];
313 }
314 
318 - (CPArray)allValues
319 {
320  var keys = self._keys,
321  index = keys.length,
322  values = [];
323 
324  while (index--)
325  values.push(self.valueForKey(keys[index]));
326 
327  return values;
328 }
329 
338 - (CPArray)allKeysForObject:(id)anObject
339 {
340  var keys = self._keys,
341  count = keys.length,
342  index = 0,
343  matchingKeys = [],
344  key = nil,
345  value = nil;
346 
347  for (; index < count; ++index)
348  {
349  key = keys[index];
350  value = self._buckets[key];
351 
352  if (value.isa && anObject && anObject.isa && [value respondsToSelector:@selector(isEqual:)] && [value isEqual:anObject])
353  matchingKeys.push(key);
354  else if (value === anObject)
355  matchingKeys.push(key);
356  }
357 
358  return matchingKeys;
359 }
360 
361 - (CPArray)keysOfEntriesPassingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
362 {
363  return [self keysOfEntriesWithOptions:CPEnumerationNormal passingTest:predicate];
364 }
365 
366 - (CPArray)keysOfEntriesWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
367 {
368  var keys = self._keys;
369 
370  if (options & CPEnumerationReverse)
371  {
372  var index = [keys count] - 1,
373  stop = -1,
374  increment = -1;
375  }
376  else
377  {
378  var index = 0,
379  stop = [keys count],
380  increment = 1;
381  }
382 
383  var matchingKeys = [],
384  key = nil,
385  value = nil,
386  shouldStop = NO,
387  stopRef = @ref(shouldStop);
388 
389  for (; index !== stop; index += increment)
390  {
391  key = keys[index];
392  value = self._buckets[key];
393 
394  if (predicate(key, value, stopRef))
395  matchingKeys.push(key);
396 
397  if (shouldStop)
398  break;
399  }
400 
401  return matchingKeys;
402 }
403 
404 - (CPArray)keysSortedByValueUsingComparator:(Function /*(id obj1, id obj2)*/)comparator
405 {
406  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
407  {
408  a = [self objectForKey:a];
409  b = [self objectForKey:b];
410 
411  return comparator(a, b);
412  }
413  ];
414 }
415 
416 - (CPArray)keysSortedByValueUsingSelector:(SEL)theSelector
417 {
418  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
419  {
420  a = [self objectForKey:a];
421  b = [self objectForKey:b];
422 
423  return [a performSelector:theSelector withObject:b];
424  }
425  ];
426 }
427 
431 - (CPEnumerator)keyEnumerator
432 {
433  return [self._keys objectEnumerator];
434 }
435 
439 - (CPEnumerator)objectEnumerator
440 {
441  return [[_CPDictionaryValueEnumerator alloc] initWithDictionary:self];
442 }
443 
447 - (BOOL)isEqualToDictionary:(CPDictionary)aDictionary
448 {
449  if (self === aDictionary)
450  return YES;
451 
452  var count = [self count];
453 
454  if (count !== [aDictionary count])
455  return NO;
456 
457  var index = count,
458  keys = self._keys;
459 
460  while (index--)
461  {
462  var currentKey = keys[index],
463  lhsObject = self._buckets[currentKey],
464  rhsObject = aDictionary._buckets[currentKey];
465 
466  if (lhsObject === rhsObject)
467  continue;
468 
469  if (lhsObject && lhsObject.isa && rhsObject && rhsObject.isa && [lhsObject respondsToSelector:@selector(isEqual:)] && [lhsObject isEqual:rhsObject])
470  continue;
471 
472  return NO;
473  }
474 
475  return YES;
476 }
477 
478 - (BOOL)isEqual:(id)anObject
479 {
480  if (self === anObject)
481  return YES;
482 
483  if (![anObject isKindOfClass:[CPDictionary class]])
484  return NO;
485 
486  return [self isEqualToDictionary:anObject];
487 }
488 
489 /*
490  Instance.allKeysForObject(anObject)
491  {
492  var i= 0,
493  keys= CPArray.array(),
494  count= this.count();
495 
496  while ((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
497 
498  return keys;
499  }
500 
501  Instance.keysSortedByValueUsingSelector(aSelector)
502  {
503  var dictionary= this,
504  objectSelector= function(rhs)
505  {
506  return aSelector.apply(dictionary.objectForKey(this), [dictionary.objectForKey(rhs)]);
507  };
508 
509  return this._keys.sortedArrayUsingSelector(objectSelector);
510  }
511 */
517 - (id)objectForKey:(id)aKey
518 {
519  var object = self._buckets[aKey];
520 
521  return (object === undefined) ? nil : object;
522 }
523 /*
524  Instance.objectsForKeys(keys, aNotFoundMarker)
525  {
526  var i= keys.length,
527  objects= CPArray.array();
528 
529  while (i--)
530  {
531  var object= this.objectForKey(keys[i]);
532  objects.addObject(object==nil?aNotFoundMarker:object);
533  }
534 
535  return objects;
536  }
537 
538  Instance.valueForKey(aKey)
539  {
540  if (aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
541 
542  return base.valueForKey(aKey);
543  }
544 */
548 - (void)removeAllObjects
549 {
550  self.removeAllValues();
551 }
552 
557 - (void)removeObjectForKey:(id)aKey
558 {
559  self.removeValueForKey(aKey);
560 }
561 
566 - (void)removeObjectsForKeys:(CPArray)keysForRemoval
567 {
568  var index = keysForRemoval.length;
569 
570  while (index--)
571  [self removeObjectForKey:keysForRemoval[index]];
572 }
573 
574 /*
575  Instance.setDictionary(aDictionary)
576  {
577  this._keys= CPArray.arrayWithArray(aDictionary.allKeys());
578  this._objects= CPArray.arrayWithArray(aDictionary.allValues());
579 
580  this._dictionary= { };
581 
582  var i= this._keys.count();
583  while (i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
584  }
585 */
591 - (void)setObject:(id)anObject forKey:(id)aKey
592 {
593  if (aKey === nil)
594  [CPException raise:CPInvalidArgumentException reason:@"key cannot be nil"];
595 
596  if (anObject === nil)
597  [CPException raise:CPInvalidArgumentException reason:@"object cannot be nil (key: " + aKey + @")"];
598 
599  self.setValueForKey(aKey, anObject);
600 }
601 
605 - (void)addEntriesFromDictionary:(CPDictionary)aDictionary
606 {
607  if (!aDictionary)
608  return;
609 
610  var keys = [aDictionary allKeys],
611  index = [keys count];
612 
613  while (index--)
614  {
615  var key = keys[index];
616 
617  [self setObject:[aDictionary objectForKey:key] forKey:key];
618  }
619 }
620 
624 - (CPString)description
625 {
626  var string = "@{",
627  keys = [self allKeys],
628  index = 0,
629  count = self._count;
630 
631  keys.sort();
632 
633  for (; index < count; ++index)
634  {
635  if (index === 0)
636  string += "\n";
637 
638  var key = keys[index],
639  value = self.valueForKey(key);
640 
641  string += " @\"" + key + "\": " + CPDescriptionOfObject(value, CPDictionaryMaxDescriptionRecursion).split("\n").join("\n ") + (index + 1 < count ? "," : "") + "\n";
642  }
643 
644  return string + "}";
645 }
646 
647 - (BOOL)containsKey:(id)aKey
648 {
649  var value = [self objectForKey:aKey];
650  return ((value !== nil) && (value !== undefined));
651 }
652 
653 - (void)enumerateKeysAndObjectsUsingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
654 {
655  var shouldStop = NO,
656  shouldStopRef = @ref(shouldStop),
657  keys = self._keys,
658  count = self._count;
659 
660  for (var index = 0; index < count; index++)
661  {
662  var key = keys[index],
663  value = self.valueForKey(key);
664 
665  aFunction(key, value, shouldStopRef);
666 
667  if (shouldStop)
668  return;
669  }
670 }
671 
672 - (void)enumerateKeysAndObjectsWithOptions:(CPEnumerationOptions)opts usingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
673 {
674  // Ignore the options because neither option has an effect.
675  // CPEnumerationReverse has no effect on enumerating a CPDictionary because dictionary enumeration is not ordered.
676  // CPEnumerationConcurrent is not possible in a single threaded environment.
677  [self enumerateKeysAndObjectsUsingBlock:aFunction];
678 }
679 
680 @end
681 
682 @implementation CPDictionary (CPCoding)
683 
684 /*
685  Initializes the dictionary by unarchiving the data from a coder.
686  @param aCoder the coder from which the data will be unarchived.
687  @return the initialized dictionary
688 */
689 - (id)initWithCoder:(CPCoder)aCoder
690 {
691  return [aCoder _decodeDictionaryOfObjectsForKey:@"CP.objects"];
692 }
693 
698 - (void)encodeWithCoder:(CPCoder)aCoder
699 {
700  [aCoder _encodeDictionaryOfObjects:self forKey:@"CP.objects"];
701 }
702 
703 @end
704 
705 
706 /* @ignore */
707 @implementation _CPDictionaryValueEnumerator : CPEnumerator
708 {
709  CPEnumerator _keyEnumerator;
710  CPDictionary _dictionary;
711 }
712 
713 - (id)initWithDictionary:(CPDictionary)aDictionary
714 {
715  self = [super init];
716 
717  if (self)
718  {
719  _keyEnumerator = [aDictionary keyEnumerator];
720  _dictionary = aDictionary;
721  }
722 
723  return self;
724 }
725 
726 - (id)nextObject
727 {
728  var key = [_keyEnumerator nextObject];
729 
730  if (key === nil)
731  return nil;
732 
733  return [_dictionary objectForKey:key];
734 }
735 
736 @end
737 
738 
747 {
748  id __doxygen__;
749 }
750 
751 @end
752 
753 CFDictionary.prototype.isa = CPDictionary;
754 CFMutableDictionary.prototype.isa = CPMutableDictionary;