API  1.0.0
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  var i = [keyArray count];
221 
222  if ([objects count] != i)
223  [CPException raise:CPInvalidArgumentException reason:[CPString stringWithFormat:@"Counts are different.(%d != %d)", [objects count], [keyArray count]]];
224 
225  if (self)
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  while (argCount-- > 2)
272  {
273  var key = arguments[argCount--],
274  value = arguments[argCount];
275 
276  if (value === nil)
277  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((argCount / 2) - 1) + @"]"];
278 
279  if (key === nil)
280  [CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((argCount / 2) - 1) + @"]"];
281 
282  [self setObject:value forKey:key];
283  }
284  }
285 
286  return self;
287 }
288 
293 {
295 }
296 
300 - (int)count
301 {
302  return self._count;
303 }
304 
308 - (CPArray)allKeys
309 {
310  return [self._keys copy];
311 }
312 
316 - (CPArray)allValues
317 {
318  var keys = self._keys,
319  index = keys.length,
320  values = [];
321 
322  while (index--)
323  values.push(self.valueForKey(keys[index]));
324 
325  return values;
326 }
327 
336 - (CPArray)allKeysForObject:(id)anObject
337 {
338  var keys = self._keys,
339  count = keys.length,
340  index = 0,
341  matchingKeys = [],
342  key = nil,
343  value = nil;
344 
345  for (; index < count; ++index)
346  {
347  key = keys[index];
348  value = self._buckets[key];
349 
350  if (value.isa && anObject && anObject.isa && [value respondsToSelector:@selector(isEqual:)] && [value isEqual:anObject])
351  matchingKeys.push(key);
352  else if (value === anObject)
353  matchingKeys.push(key);
354  }
355 
356  return matchingKeys;
357 }
358 
359 - (CPArray)keysOfEntriesPassingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
360 {
361  return [self keysOfEntriesWithOptions:CPEnumerationNormal passingTest:predicate];
362 }
363 
364 - (CPArray)keysOfEntriesWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id key, id obj, @ref BOOL stop)*/)predicate
365 {
366  var keys = self._keys;
367 
368  if (options & CPEnumerationReverse)
369  {
370  var index = [keys count] - 1,
371  stop = -1,
372  increment = -1;
373  }
374  else
375  {
376  var index = 0,
377  stop = [keys count],
378  increment = 1;
379  }
380 
381  var matchingKeys = [],
382  key = nil,
383  value = nil,
384  shouldStop = NO,
385  stopRef = @ref(shouldStop);
386 
387  for (; index !== stop; index += increment)
388  {
389  key = keys[index];
390  value = self._buckets[key];
391 
392  if (predicate(key, value, stopRef))
393  matchingKeys.push(key);
394 
395  if (shouldStop)
396  break;
397  }
398 
399  return matchingKeys;
400 }
401 
402 - (CPArray)keysSortedByValueUsingComparator:(Function /*(id obj1, id obj2)*/)comparator
403 {
404  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
405  {
406  a = [self objectForKey:a];
407  b = [self objectForKey:b];
408 
409  return comparator(a, b);
410  }
411  ];
412 }
413 
414 - (CPArray)keysSortedByValueUsingSelector:(SEL)theSelector
415 {
416  return [[self allKeys] sortedArrayUsingFunction:function(a, b)
417  {
418  a = [self objectForKey:a];
419  b = [self objectForKey:b];
420 
421  return [a performSelector:theSelector withObject:b];
422  }
423  ];
424 }
425 
429 - (CPEnumerator)keyEnumerator
430 {
431  return [self._keys objectEnumerator];
432 }
433 
437 - (CPEnumerator)objectEnumerator
438 {
439  return [[_CPDictionaryValueEnumerator alloc] initWithDictionary:self];
440 }
441 
445 - (BOOL)isEqualToDictionary:(CPDictionary)aDictionary
446 {
447  if (self === aDictionary)
448  return YES;
449 
450  var count = [self count];
451 
452  if (count !== [aDictionary count])
453  return NO;
454 
455  var index = count,
456  keys = self._keys;
457 
458  while (index--)
459  {
460  var currentKey = keys[index],
461  lhsObject = self._buckets[currentKey],
462  rhsObject = aDictionary._buckets[currentKey];
463 
464  if (lhsObject === rhsObject)
465  continue;
466 
467  if (lhsObject && lhsObject.isa && rhsObject && rhsObject.isa && [lhsObject respondsToSelector:@selector(isEqual:)] && [lhsObject isEqual:rhsObject])
468  continue;
469 
470  return NO;
471  }
472 
473  return YES;
474 }
475 
476 - (BOOL)isEqual:(id)anObject
477 {
478  if (self === anObject)
479  return YES;
480 
481  if (![anObject isKindOfClass:[CPDictionary class]])
482  return NO;
483 
484  return [self isEqualToDictionary:anObject];
485 }
486 
487 /*
488  Instance.allKeysForObject(anObject)
489  {
490  var i= 0,
491  keys= CPArray.array(),
492  count= this.count();
493 
494  while ((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
495 
496  return keys;
497  }
498 
499  Instance.keysSortedByValueUsingSelector(aSelector)
500  {
501  var dictionary= this,
502  objectSelector= function(rhs)
503  {
504  return aSelector.apply(dictionary.objectForKey(this), [dictionary.objectForKey(rhs)]);
505  };
506 
507  return this._keys.sortedArrayUsingSelector(objectSelector);
508  }
509 */
515 - (id)objectForKey:(id)aKey
516 {
517  var object = self._buckets[aKey];
518 
519  return (object === undefined) ? nil : object;
520 }
521 /*
522  Instance.objectsForKeys(keys, aNotFoundMarker)
523  {
524  var i= keys.length,
525  objects= CPArray.array();
526 
527  while (i--)
528  {
529  var object= this.objectForKey(keys[i]);
530  objects.addObject(object==nil?aNotFoundMarker:object);
531  }
532 
533  return objects;
534  }
535 
536  Instance.valueForKey(aKey)
537  {
538  if (aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
539 
540  return base.valueForKey(aKey);
541  }
542 */
546 - (void)removeAllObjects
547 {
548  self.removeAllValues();
549 }
550 
555 - (void)removeObjectForKey:(id)aKey
556 {
557  self.removeValueForKey(aKey);
558 }
559 
564 - (void)removeObjectsForKeys:(CPArray)keysForRemoval
565 {
566  var index = keysForRemoval.length;
567 
568  while (index--)
569  [self removeObjectForKey:keysForRemoval[index]];
570 }
571 
572 /*
573  Instance.setDictionary(aDictionary)
574  {
575  this._keys= CPArray.arrayWithArray(aDictionary.allKeys());
576  this._objects= CPArray.arrayWithArray(aDictionary.allValues());
577 
578  this._dictionary= { };
579 
580  var i= this._keys.count();
581  while (i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
582  }
583 */
589 - (void)setObject:(id)anObject forKey:(id)aKey
590 {
591  if (aKey === nil)
592  [CPException raise:CPInvalidArgumentException reason:@"key cannot be nil"];
593 
594  if (anObject === nil)
595  [CPException raise:CPInvalidArgumentException reason:@"object cannot be nil (key: " + aKey + @")"];
596 
597  self.setValueForKey(aKey, anObject);
598 }
599 
603 - (void)addEntriesFromDictionary:(CPDictionary)aDictionary
604 {
605  if (!aDictionary)
606  return;
607 
608  var keys = [aDictionary allKeys],
609  index = [keys count];
610 
611  while (index--)
612  {
613  var key = keys[index];
614 
615  [self setObject:[aDictionary objectForKey:key] forKey:key];
616  }
617 }
618 
623 {
624  var string = "@{",
625  keys = [self allKeys],
626  index = 0,
627  count = self._count;
628 
629  keys.sort();
630 
631  for (; index < count; ++index)
632  {
633  if (index === 0)
634  string += "\n";
635 
636  var key = keys[index],
637  value = self.valueForKey(key);
638 
639  string += " @\"" + key + "\": " + CPDescriptionOfObject(value, CPDictionaryMaxDescriptionRecursion).split("\n").join("\n ") + (index + 1 < count ? "," : "") + "\n";
640  }
641 
642  return string + "}";
643 }
644 
645 - (BOOL)containsKey:(id)aKey
646 {
647  var value = [self objectForKey:aKey];
648  return ((value !== nil) && (value !== undefined));
649 }
650 
651 - (void)enumerateKeysAndObjectsUsingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
652 {
653  var shouldStop = NO,
654  shouldStopRef = @ref(shouldStop),
655  keys = self._keys,
656  count = self._count;
657 
658  for (var index = 0; index < count; index++)
659  {
660  var key = keys[index],
661  value = self.valueForKey(key);
662 
663  aFunction(key, value, shouldStopRef);
664 
665  if (shouldStop)
666  return;
667  }
668 }
669 
670 - (void)enumerateKeysAndObjectsWithOptions:(CPEnumerationOptions)opts usingBlock:(Function /*(id aKey, id anObject, @ref BOOL stop)*/)aFunction
671 {
672  // Ignore the options because neither option has an effect.
673  // CPEnumerationReverse has no effect on enumerating a CPDictionary because dictionary enumeration is not ordered.
674  // CPEnumerationConcurrent is not possible in a single threaded environment.
675  [self enumerateKeysAndObjectsUsingBlock:aFunction];
676 }
677 
678 @end
679 
680 @implementation CPDictionary (CPCoding)
681 
682 /*
683  Initializes the dictionary by unarchiving the data from a coder.
684  @param aCoder the coder from which the data will be unarchived.
685  @return the initialized dictionary
686 */
687 - (id)initWithCoder:(CPCoder)aCoder
688 {
689  return [aCoder _decodeDictionaryOfObjectsForKey:@"CP.objects"];
690 }
691 
696 - (void)encodeWithCoder:(CPCoder)aCoder
697 {
698  [aCoder _encodeDictionaryOfObjects:self forKey:@"CP.objects"];
699 }
700 
701 @end
702 
703 
704 /* @ignore */
705 @implementation _CPDictionaryValueEnumerator : CPEnumerator
706 {
707  CPEnumerator _keyEnumerator;
708  CPDictionary _dictionary;
709 }
710 
711 - (id)initWithDictionary:(CPDictionary)aDictionary
712 {
713  self = [super init];
714 
715  if (self)
716  {
717  _keyEnumerator = [aDictionary keyEnumerator];
718  _dictionary = aDictionary;
719  }
720 
721  return self;
722 }
723 
724 - (id)nextObject
725 {
726  var key = [_keyEnumerator nextObject];
727 
728  if (key === nil)
729  return nil;
730 
731  return [_dictionary objectForKey:key];
732 }
733 
734 @end
735 
736 
745 {
746  id __doxygen__;
747 }
748 
749 @end
750 
751 CFDictionary.prototype.isa = CPDictionary;
752 CFMutableDictionary.prototype.isa = CPMutableDictionary;
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
BOOL isEqualToDictionary:(CPDictionary aDictionary)
Definition: CPDictionary.j:445
var isEqual
An object representation of nil.
Definition: CPNull.h:2
function CPDescriptionOfObject(anObject, maximumRecursionDepth)
Definition: CPObject.j:603
CPEnumerator keyEnumerator()
Definition: CPDictionary.j:429
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
void removeObjectForKey:(id aKey)
Definition: CPDictionary.j:555
id dictionaryWithJSObject:recursively:(JSObject object, [recursively] BOOL recursively)
Definition: CPDictionary.j:113
A mutable key-value pair collection.
Definition: CPDictionary.h:2
FrameUpdater prototype stop
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
Defines an interface for enumerators.
Definition: CPEnumerator.h:2
id objectForKey:(id aKey)
Definition: CPDictionary.j:515
CPArray keysOfEntriesWithOptions:passingTest:(CPEnumerationOptions options, [passingTest] Function/*(id key, id obj, @ref BOOL stop) */predicate)
Definition: CPDictionary.j:364
CPEnumerator objectEnumerator()
Definition: CPDictionary.j:437
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
id init()
Definition: CPObject.j:145
void enumerateKeysAndObjectsUsingBlock:(Function/*(id aKey, id anObject, @ref BOOL stop) */aFunction)
Definition: CPDictionary.j:651
Class class()
Definition: CPObject.j:179
var CPDictionaryMaxDescriptionRecursion
Definition: CPDictionary.j:24
CPArray allKeys()
Definition: CPDictionary.j:308
id initWithObjects:forKeys:(CPArray objects, [forKeys] CPArray keyArray)
Definition: CPDictionary.j:216
void setObject:forKey:(id anObject, [forKey] id aKey)
Definition: CPDictionary.j:589
id dictionaryWithDictionary:(CPDictionary aDictionary)
Definition: CPDictionary.j:70
CompletionHandlerAgent prototype increment
CPDictionary copy()
Definition: CPDictionary.j:292
id stringWithFormat:(CPString format, [,] ...)
Definition: CPString.j:166
FrameUpdater prototype description