API  0.9.6
 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 #import "Ref.h"
24 
25 /* @ignore */
26 @implementation _CPDictionaryValueEnumerator : CPEnumerator
27 {
28  CPEnumerator _keyEnumerator;
29  CPDictionary _dictionary;
30 }
31 
32 - (id)initWithDictionary:(CPDictionary)aDictionary
33 {
34  self = [super init];
35 
36  if (self)
37  {
38  _keyEnumerator = [aDictionary keyEnumerator];
39  _dictionary = aDictionary;
40  }
41 
42  return self;
43 }
44 
45 - (id)nextObject
46 {
47  var key = [_keyEnumerator nextObject];
48 
49  if (key === nil)
50  return nil;
51 
52  return [_dictionary objectForKey:key];
53 }
54 
55 @end
56 
74 @implementation CPDictionary : CPObject
75 {
76  id __doxygen__;
77 }
78 
79 /*
80  @ignore
81 */
82 + (id)alloc
83 {
84  var result = new CFMutableDictionary();
85  result.isa = [self class];
86  return result;
87 }
88 
92 + (id)dictionary
93 {
94  return [[self alloc] init];
95 }
96 
102 + (id)dictionaryWithDictionary:(CPDictionary)aDictionary
103 {
104  return [[self alloc] initWithDictionary:aDictionary];
105 }
106 
113 + (id)dictionaryWithObject:(id)anObject forKey:(id)aKey
114 {
115  return [[self alloc] initWithObjects:[anObject] forKeys:[aKey]];
116 }
117 
125 + (id)dictionaryWithObjects:(CPArray)objects forKeys:(CPArray)keys
126 {
127  return [[self alloc] initWithObjects:objects forKeys:keys];
128 }
129 
135 + (id)dictionaryWithJSObject:(JSObject)object
136 {
137  return [self dictionaryWithJSObject:object recursively:NO];
138 }
139 
145 + (id)dictionaryWithJSObject:(JSObject)object recursively:(BOOL)recursively
146 {
147  var key = "",
148  dictionary = [[self alloc] init];
149 
150  for (key in object)
151  {
152  if (!object.hasOwnProperty(key))
153  continue;
154 
155  var value = object[key];
156 
157  if (value === null)
158  {
159  [dictionary setObject:[CPNull null] forKey:key];
160  continue;
161  }
162 
163  if (recursively)
164  {
165  if (value.constructor === Object)
166  value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
167  else if ([value isKindOfClass:CPArray])
168  {
169  var newValue = [],
170  i = 0,
171  count = value.length;
172 
173  for (; i < count; i++)
174  {
175  var thisValue = value[i];
176 
177  if (thisValue === null)
178  {
179  newValue.push([CPNull null]);
180  }
181  else
182  {
183  if (thisValue.constructor === Object)
184  newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
185  else
186  newValue.push(thisValue);
187  }
188  }
189 
190  value = newValue;
191  }
192  }
193 
194  [dictionary setObject:value forKey:key];
195  }
196 
197  return dictionary;
198 }
199 
217 + (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...
218 {
219  arguments[0] = [self alloc];
220  arguments[1] = @selector(initWithObjectsAndKeys:);
221 
222  return objj_msgSend.apply(this, arguments);
223 }
224 
230 - (id)initWithDictionary:(CPDictionary)aDictionary
231 {
232  var key = "",
233  dictionary = [[CPDictionary alloc] init];
234 
235  for (key in aDictionary._buckets)
236  [dictionary setObject:[aDictionary objectForKey:key] forKey:key];
237 
238  return dictionary;
239 }
240 
248 - (id)initWithObjects:(CPArray)objects forKeys:(CPArray)keyArray
249 {
250  self = [super init];
251 
252  if ([objects count] != [keyArray count])
253  [CPException raise:CPInvalidArgumentException reason:"Counts are different.(" + [objects count] + "!=" + [keyArray count] + ")"];
254 
255  if (self)
256  {
257  var i = [keyArray count];
258 
259  while (i--)
260  [self setObject:objects[i] forKey:keyArray[i]];
261  }
262 
263  return self;
264 }
265 
280 - (id)initWithObjectsAndKeys:(id)firstObject, ...
281 {
282  var argCount = arguments.length;
283 
284  if (argCount % 2 !== 0)
285  [CPException raise:CPInvalidArgumentException reason:"Key-value count is mismatched. (" + argCount + " arguments passed)"];
286 
287  self = [super init];
288 
289  if (self)
290  {
291  // The arguments array contains self and _cmd, so the first object is at position 2.
292  var index = 2;
293 
294  for (; index < argCount; index += 2)
295  {
296  var value = arguments[index];
297 
298  if (value === nil)
299  break;
300 
301  [self setObject:value forKey:arguments[index + 1]];
302  }
303  }
304 
305  return self;
306 }
307 
312 {
314 }
315 
319 - (int)count
320 {
321  return _count;
322 }
323 
327 - (CPArray)allKeys
328 {
329  return [_keys copy];
330 }
331 
335 - (CPArray)allValues
336 {
337  var index = _keys.length,
338  values = [];
339 
340  while (index--)
341  values.push(self.valueForKey(_keys[index]));
342 
343  return values;
344 }
345 
354 - (CPArray)allKeysForObject:(id)anObject
355 {
356  var count = _keys.length,
357  index = 0,
358  matchingKeys = [],
359  key = nil,
360  value = nil;
361 
362  for (; index < count; ++index)
363  {
364  key = _keys[index];
365  value = _buckets[key];
366 
367  if (value.isa && anObject && anObject.isa && [value respondsToSelector:@selector(isEqual:)] && [value isEqual:anObject])
368  matchingKeys.push(key);
369  else if (value === anObject)
370  matchingKeys.push(key);
371  }
372 
373  return matchingKeys;
374 }
375 
376 - (CPArray)keysOfEntriesPassingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
377 {
378  return [self keysOfEntriesWithOptions:CPEnumerationNormal passingTest:predicate];
379 }
380 
381 - (CPArray)keysOfEntriesWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
382 {
383  if (options & CPEnumerationReverse)
384  {
385  var index = [_keys count] - 1,
386  stop = -1,
387  increment = -1;
388  }
389  else
390  {
391  var index = 0,
392  stop = [_keys count],
393  increment = 1;
394  }
395 
396  var matchingKeys = [],
397  key = nil,
398  value = nil,
399  shouldStop = NO,
400  stopRef = AT_REF(shouldStop);
401 
402  for (; index !== stop; index += increment)
403  {
404  key = _keys[index];
405  value = _buckets[key];
406 
407  if (predicate(key, value, stopRef))
408  matchingKeys.push(key);
409 
410  if (shouldStop)
411  break;
412  }
413 
414  return matchingKeys;
415 }
416 
417 - (CPArray)keysSortedByValueUsingComparator:(Function /*(id obj1, id obj2)*/)comparator
418 {
419  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
420  {
421  a = [self objectForKey:a];
422  b = [self objectForKey:b];
423 
424  return comparator(a, b);
425  }
426  ];
427 }
428 
429 - (CPArray)keysSortedByValueUsingSelector:(SEL)theSelector
430 {
431  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
432  {
433  a = [self objectForKey:a];
434  b = [self objectForKey:b];
435 
436  return [a performSelector:theSelector withObject:b];
437  }
438  ];
439 }
440 
444 - (CPEnumerator)keyEnumerator
445 {
446  return [_keys objectEnumerator];
447 }
448 
452 - (CPEnumerator)objectEnumerator
453 {
454  return [[_CPDictionaryValueEnumerator alloc] initWithDictionary:self];
455 }
456 
460 - (BOOL)isEqualToDictionary:(CPDictionary)aDictionary
461 {
462  if (self === aDictionary)
463  return YES;
464 
465  var count = [self count];
466 
467  if (count !== [aDictionary count])
468  return NO;
469 
470  var index = count;
471 
472  while (index--)
473  {
474  var currentKey = _keys[index],
475  lhsObject = _buckets[currentKey],
476  rhsObject = aDictionary._buckets[currentKey];
477 
478  if (lhsObject === rhsObject)
479  continue;
480 
481  if (lhsObject && lhsObject.isa && rhsObject && rhsObject.isa && [lhsObject respondsToSelector:@selector(isEqual:)] && [lhsObject isEqual:rhsObject])
482  continue;
483 
484  return NO;
485  }
486 
487  return YES;
488 }
489 
490 - (BOOL)isEqual:(id)anObject
491 {
492  if (self === anObject)
493  return YES;
494 
495  if (![anObject isKindOfClass:[CPDictionary class]])
496  return NO;
497 
498  return [self isEqualToDictionary:anObject];
499 }
500 
501 /*
502  Instance.allKeysForObject(anObject)
503  {
504  var i= 0,
505  keys= CPArray.array(),
506  count= this.count();
507 
508  while ((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
509 
510  return keys;
511  }
512 
513  Instance.keysSortedByValueUsingSelector(aSelector)
514  {
515  var dictionary= this,
516  objectSelector= function(rhs)
517  {
518  return aSelector.apply(dictionary.objectForKey(this), [dictionary.objectForKey(rhs)]);
519  };
520 
521  return this._keys.sortedArrayUsingSelector(objectSelector);
522  }
523 */
529 - (id)objectForKey:(id)aKey
530 {
531  var object = _buckets[aKey];
532 
533  return (object === undefined) ? nil : object;
534 }
535 /*
536  Instance.objectsForKeys(keys, aNotFoundMarker)
537  {
538  var i= keys.length,
539  objects= CPArray.array();
540 
541  while (i--)
542  {
543  var object= this.objectForKey(keys[i]);
544  objects.addObject(object==nil?aNotFoundMarker:object);
545  }
546 
547  return objects;
548  }
549 
550  Instance.valueForKey(aKey)
551  {
552  if (aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
553 
554  return base.valueForKey(aKey);
555  }
556 */
560 - (void)removeAllObjects
561 {
562  self.removeAllValues();
563 }
564 
569 - (void)removeObjectForKey:(id)aKey
570 {
571  self.removeValueForKey(aKey);
572 }
573 
578 - (void)removeObjectsForKeys:(CPArray)keysForRemoval
579 {
580  var index = keysForRemoval.length;
581 
582  while (index--)
583  [self removeObjectForKey:keysForRemoval[index]];
584 }
585 
586 /*
587  Instance.setDictionary(aDictionary)
588  {
589  this._keys= CPArray.arrayWithArray(aDictionary.allKeys());
590  this._objects= CPArray.arrayWithArray(aDictionary.allValues());
591 
592  this._dictionary= { };
593 
594  var i= this._keys.count();
595  while (i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
596  }
597 */
603 - (void)setObject:(id)anObject forKey:(id)aKey
604 {
605  self.setValueForKey(aKey, anObject);
606 }
607 
611 - (void)addEntriesFromDictionary:(CPDictionary)aDictionary
612 {
613  if (!aDictionary)
614  return;
615 
616  var keys = [aDictionary allKeys],
617  index = [keys count];
618 
619  while (index--)
620  {
621  var key = keys[index];
622 
623  [self setObject:[aDictionary objectForKey:key] forKey:key];
624  }
625 }
626 
630 - (CPString)description
631 {
632  var string = "@{\n",
633  keys = _keys,
634  index = 0,
635  count = _count;
636 
637  for (; index < count; ++index)
638  {
639  var key = keys[index],
640  value = valueForKey(key);
641 
642  string += "\t" + key + ": " + CPDescriptionOfObject(value).split('\n').join("\n\t") + ",\n";
643  }
644 
645  return string + "}";
646 }
647 
648 - (BOOL)containsKey:(id)aKey
649 {
650  var value = [self objectForKey:aKey];
651  return ((value !== nil) && (value !== undefined));
652 }
653 
654 - (void)enumerateKeysAndObjectsUsingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
655 {
656  var shouldStop = NO,
657  shouldStopRef = AT_REF(shouldStop);
658 
659  for (var index = 0; index < _count; index++)
660  {
661  var key = _keys[index],
662  value = valueForKey(key);
663 
664  aFunction(key, value, shouldStopRef);
665 
666  if (shouldStop)
667  return;
668  }
669 }
670 
671 - (void)enumerateKeysAndObjectsWithOptions:(CPEnumerationOptions)opts usingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
672 {
673  // Ignore the options because neither option has an effect.
674  // CPEnumerationReverse has no effect on enumerating a CPDictionary because dictionary enumeration is not ordered.
675  // CPEnumerationConcurrent is not possible in a single threaded environment.
676  [self enumerateKeysAndObjectsUsingBlock:aFunction];
677 }
678 
679 @end
680 
681 @implementation CPDictionary (CPCoding)
682 
683 /*
684  Initializes the dictionary by unarchiving the data from a coder.
685  @param aCoder the coder from which the data will be unarchived.
686  @return the initialized dictionary
687 */
688 - (id)initWithCoder:(CPCoder)aCoder
689 {
690  return [aCoder _decodeDictionaryOfObjectsForKey:@"CP.objects"];
691 }
692 
697 - (void)encodeWithCoder:(CPCoder)aCoder
698 {
699  [aCoder _encodeDictionaryOfObjects:self forKey:@"CP.objects"];
700 }
701 
702 @end
703 
713 {
714  id __doxygen__;
715 }
716 
717 @end
718 
719 CFDictionary.prototype.isa = CPDictionary;
720 CFMutableDictionary.prototype.isa = CPMutableDictionary;