API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPArray.j
Go to the documentation of this file.
1 /*
2  * CPArray.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 
28 
32 
33 var concat = Array.prototype.concat,
34  join = Array.prototype.join,
35  push = Array.prototype.push;
36 
37 #define FORWARD_TO_CONCRETE_CLASS()\
38  if (self === _CPSharedPlaceholderArray)\
39  {\
40  arguments[0] = [_CPJavaScriptArray alloc];\
41  return objj_msgSend.apply(this, arguments);\
42  }\
43  return [super init];
44 
56 @implementation CPArray : CPObject
57 {
58  id __doxygen__;
59 }
60 
64 + (id)alloc
65 {
66  if (self === CPArray || self === CPMutableArray)
67  return [_CPPlaceholderArray alloc];
68 
69  return [super alloc];
70 }
71 
75 + (id)array
76 {
77  return [[self alloc] init];
78 }
79 
85 + (id)arrayWithArray:(CPArray)anArray
86 {
87  return [[self alloc] initWithArray:anArray];
88 }
89 
95 + (id)arrayWithObject:(id)anObject
96 {
97  return [[self alloc] initWithObjects:anObject];
98 }
99 
105 + (id)arrayWithObjects:(id)anObject, ...
106 {
107  arguments[0] = [self alloc];
108  arguments[1] = @selector(initWithObjects:);
109 
110  return objj_msgSend.apply(this, arguments);
111 }
112 
119 + (id)arrayWithObjects:(id)objects count:(unsigned)aCount
120 {
121  return [[self alloc] initWithObjects:objects count:aCount];
122 }
123 
128 - (id)init
129 {
131 }
132 
133 // Creating an Array
139 - (id)initWithArray:(CPArray)anArray
140 {
142 }
143 
152 - (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems
153 {
155 }
156 
160 - (id)initWithObjects:(id)anObject, ...
161 {
163 }
164 
171 - (id)initWithObjects:(id)objects count:(unsigned)aCount
172 {
174 }
175 
176 // FIXME: This should be defined in CPMutableArray, not here.
177 - (id)initWithCapacity:(unsigned)aCapacity
178 {
180 }
181 
182 // Querying an array
187 - (BOOL)containsObject:(id)anObject
188 {
189  return [self indexOfObject:anObject] !== CPNotFound;
190 }
191 
192 - (BOOL)containsObjectIdenticalTo:(id)anObject
193 {
194  return [self indexOfObjectIdenticalTo:anObject] !== CPNotFound;
195 }
196 
200 - (int)count
201 {
202  _CPRaiseInvalidAbstractInvocation(self, _cmd);
203 }
204 
208 - (id)firstObject
209 {
210  var count = [self count];
211 
212  if (count > 0)
213  return [self objectAtIndex:0];
214 
215  return nil;
216 }
217 
221 - (id)lastObject
222 {
223  var count = [self count];
224 
225  if (count <= 0)
226  return nil;
227 
228  return [self objectAtIndex:count - 1];
229 }
230 
235 - (id)objectAtIndex:(int)anIndex
236 {
237  _CPRaiseInvalidAbstractInvocation(self, _cmd);
238 }
239 
245 - (CPArray)objectsAtIndexes:(CPIndexSet)indexes
246 {
247  var index = CPNotFound,
248  objects = [];
249 
250  while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound)
251  objects.push([self objectAtIndex:index]);
252 
253  return objects;
254 }
255 
261 - (CPEnumerator)objectEnumerator
262 {
263  return [[_CPArrayEnumerator alloc] initWithArray:self];
264 }
265 
271 - (CPEnumerator)reverseObjectEnumerator
272 {
273  return [[_CPReverseArrayEnumerator alloc] initWithArray:self];
274 }
275 
283 - (CPUInteger)indexOfObject:(id)anObject
284 {
285  return [self indexOfObject:anObject inRange:nil];
286 }
287 
296 - (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange
297 {
298  // Only use isEqual: if our object is a CPObject.
299  if (anObject && anObject.isa)
300  {
301  var index = aRange ? aRange.location : 0,
302  count = aRange ? CPMaxRange(aRange) : [self count];
303 
304  for (; index < count; ++index)
305  if ([[self objectAtIndex:index] isEqual:anObject])
306  return index;
307 
308  return CPNotFound;
309  }
310 
311  return [self indexOfObjectIdenticalTo:anObject inRange:aRange];
312 }
313 
319 - (CPUInteger)indexOfObjectIdenticalTo:(id)anObject
320 {
321  return [self indexOfObjectIdenticalTo:anObject inRange:nil];
322 }
323 
332 - (CPUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange
333 {
334  var index = aRange ? aRange.location : 0,
335  count = aRange ? CPMaxRange(aRange) : [self count];
336 
337  for (; index < count; ++index)
338  if ([self objectAtIndex:index] === anObject)
339  return index;
340 
341  return CPNotFound;
342 }
343 
352 - (unsigned)indexOfObjectPassingTest:(Function /*(id anObject, int idx)*/)aPredicate
353 {
354  return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:undefined];
355 }
356 
366 - (unsigned)indexOfObjectPassingTest:(Function /*(id anObject, int idx, id context)*/)aPredicate context:(id)aContext
367 {
368  return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:aContext];
369 }
370 
381 - (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id anObject, int idx)*/)aPredicate
382 {
383  return [self indexOfObjectWithOptions:options passingTest:aPredicate context:undefined];
384 }
385 
397 - (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id anObject, int idx, id context)*/)aPredicate context:(id)aContext
398 {
399  // We don't use an enumerator because they return nil to indicate end of enumeration,
400  // but nil may actually be the value we are looking for, so we have to loop over the array.
401  if (options & CPEnumerationReverse)
402  {
403  var index = [self count] - 1,
404  stop = -1,
405  increment = -1;
406  }
407  else
408  {
409  var index = 0,
410  stop = [self count],
411  increment = 1;
412  }
413 
414  for (; index !== stop; index += increment)
415  if (aPredicate([self objectAtIndex:index], index, aContext))
416  return index;
417 
418  return CPNotFound;
419 }
420 
421 - (CPUInteger)indexOfObject:(id)anObject
422  inSortedRange:(CPRange)aRange
423  options:(CPBinarySearchingOptions)options
424  usingComparator:(Function)aComparator
425 {
426  // FIXME: comparator is not a function
427  if (!aComparator)
428  _CPRaiseInvalidArgumentException(self, _cmd, "comparator is nil");
429 
430  if ((options & CPBinarySearchingFirstEqual) && (options & CPBinarySearchingLastEqual))
431  _CPRaiseInvalidArgumentException(self, _cmd,
432  "both CPBinarySearchingFirstEqual and CPBinarySearchingLastEqual options cannot be specified");
433 
434  var count = [self count];
435 
436  if (count <= 0)
437  return (options & CPBinarySearchingInsertionIndex) ? 0 : CPNotFound;
438 
439  var first = aRange ? aRange.location : 0,
440  last = (aRange ? CPMaxRange(aRange) : [self count]) - 1;
441 
442  if (first < 0)
443  _CPRaiseRangeException(self, _cmd, first, count);
444 
445  if (last >= count)
446  _CPRaiseRangeException(self, _cmd, last, count);
447 
448  while (first <= last)
449  {
450  var middle = FLOOR((first + last) / 2),
451  result = aComparator(anObject, [self objectAtIndex:middle]);
452 
453  if (result > 0)
454  first = middle + 1;
455 
456  else if (result < 0)
457  last = middle - 1;
458 
459  else
460  {
461  if (options & CPBinarySearchingFirstEqual)
462  while (middle > first && aComparator(anObject, [self objectAtIndex:middle - 1]) === CPOrderedSame)
463  --middle;
464 
465  else if (options & CPBinarySearchingLastEqual)
466  {
467  while (middle < last && aComparator(anObject, [self objectAtIndex:middle + 1]) === CPOrderedSame)
468  ++middle;
469 
470  if (options & CPBinarySearchingInsertionIndex)
471  ++middle;
472  }
473 
474  return middle;
475  }
476  }
477 
478  if (options & CPBinarySearchingInsertionIndex)
479  return MAX(first, 0);
480 
481  return CPNotFound;
482 }
483 
492 - (CPIndexSet)indexesOfObjectsPassingTest:(Function /*(id anObject, int idx)*/)aPredicate
493 {
494  return [self indexesOfObjectsWithOptions:CPEnumerationNormal passingTest:aPredicate context:undefined];
495 }
496 
506 - (CPIndexSet)indexesOfObjectsPassingTest:(Function /*(id anObject, int idx, id context)*/)aPredicate context:(id)aContext
507 {
508  return [self indexesOfObjectsWithOptions:CPEnumerationNormal passingTest:aPredicate context:aContext];
509 }
510 
521 - (CPIndexSet)indexesOfObjectsWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id anObject, int idx)*/)aPredicate
522 {
523  return [self indexesOfObjectsWithOptions:options passingTest:aPredicate context:undefined];
524 }
525 
537 - (CPIndexSet)indexesOfObjectsWithOptions:(CPEnumerationOptions)options passingTest:(Function /*(id anObject, int idx, id context)*/)aPredicate context:(id)aContext
538 {
539  // We don't use an enumerator because they return nil to indicate end of enumeration,
540  // but nil may actually be the value we are looking for, so we have to loop over the array.
541  if (options & CPEnumerationReverse)
542  {
543  var index = [self count] - 1,
544  stop = -1,
545  increment = -1;
546  }
547  else
548  {
549  var index = 0,
550  stop = [self count],
551  increment = 1;
552  }
553 
554  var indexes = [CPIndexSet indexSet];
555 
556  for (; index !== stop; index += increment)
557  if (aPredicate([self objectAtIndex:index], index, aContext))
558  [indexes addIndex:index];
559 
560  return indexes;
561 }
562 
563 // Sending messages to elements
569 - (void)makeObjectsPerformSelector:(SEL)aSelector
570 {
571  [self makeObjectsPerformSelector:aSelector withObjects:nil];
572 }
573 
580 - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject
581 {
582  return [self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
583 }
584 
585 - (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
586 {
587  if (!aSelector)
588  [CPException raise:CPInvalidArgumentException
589  reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"];
590 
591  var index = 0,
592  count = [self count];
593 
594  if ([objects count])
595  {
596  var argumentsArray = [[nil, aSelector] arrayByAddingObjectsFromArray:objects];
597 
598  for (; index < count; ++index)
599  {
600  argumentsArray[0] = [self objectAtIndex:index];
601  objj_msgSend.apply(this, argumentsArray);
602  }
603  }
604 
605  else
606  for (; index < count; ++index)
607  objj_msgSend([self objectAtIndex:index], aSelector);
608 }
609 
610 - (void)enumerateObjectsUsingBlock:(Function /*(id anObject, int idx, @ref BOOL stop)*/)aFunction
611 {
612  // This could have been [self enumerateObjectsWithOptions:CPEnumerationNormal usingBlock:aFunction]
613  // but this method should be as fast as possible.
614  var index = 0,
615  count = [self count],
616  shouldStop = NO,
617  shouldStopRef = AT_REF(shouldStop);
618 
619  for (; index < count; ++index)
620  {
621  aFunction([self objectAtIndex:index], index, shouldStopRef);
622  if (shouldStop)
623  return;
624  }
625 }
626 
627 - (void)enumerateObjectsWithOptions:(CPEnumerationOptions)options usingBlock:(Function /*(id anObject, int idx, @ref BOOL stop)*/)aFunction
628 {
629  var shouldStop = NO;
630 
631  if (options & CPEnumerationReverse)
632  {
633  var index = [self count] - 1,
634  stop = -1,
635  increment = -1;
636  }
637  else
638  {
639  var index = 0,
640  stop = [self count],
641  increment = 1;
642  }
643 
644  for (; index !== stop; index += increment)
645  {
646  aFunction([self objectAtIndex:index], index, AT_REF(shouldStop));
647  if (shouldStop)
648  return;
649  }
650 }
651 
652 // Comparing arrays
658 - (id)firstObjectCommonWithArray:(CPArray)anArray
659 {
660  var count = [self count];
661 
662  if (![anArray count] || !count)
663  return nil;
664 
665  var index = 0;
666 
667  for (; index < count; ++index)
668  {
669  var object = [self objectAtIndex:index];
670 
671  if ([anArray containsObject:object])
672  return object;
673  }
674 
675  return nil;
676 }
677 
681 - (BOOL)isEqualToArray:(id)anArray
682 {
683  if (self === anArray)
684  return YES;
685 
686  if (![anArray isKindOfClass:CPArray])
687  return NO;
688 
689  var count = [self count],
690  otherCount = [anArray count];
691 
692  if (anArray === nil || count !== otherCount)
693  return NO;
694 
695  var index = 0;
696 
697  for (; index < count; ++index)
698  {
699  var lhs = [self objectAtIndex:index],
700  rhs = [anArray objectAtIndex:index];
701 
702  // If they're not equal, and either doesn't have an isa, or they're !isEqual (not isEqual)
703  if (lhs !== rhs && (lhs && !lhs.isa || rhs && !rhs.isa || ![lhs isEqual:rhs]))
704  return NO;
705  }
706 
707  return YES;
708 }
709 
710 - (BOOL)isEqual:(id)anObject
711 {
712  return (self === anObject) || [self isEqualToArray:anObject];
713 }
714 
715 - (Array)_javaScriptArrayCopy
716 {
717  var index = 0,
718  count = [self count],
719  copy = [];
720 
721  for (; index < count; ++index)
722  push.call(copy, [self objectAtIndex:index]);
723 
724  return copy;
725 }
726 
727 // Deriving new arrays
734 - (CPArray)arrayByAddingObject:(id)anObject
735 {
736  var argumentArray = [self _javaScriptArrayCopy];
737 
738  // We push instead of concat,because concat flattens arrays, so if the object
739  // passed in is an array, we end up with its contents added instead of itself.
740  push.call(argumentArray, anObject);
741 
742  return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
743 }
744 
749 - (CPArray)arrayByAddingObjectsFromArray:(CPArray)anArray
750 {
751  if (!anArray)
752  return [self copy];
753 
754  var anArray = anArray.isa === _CPJavaScriptArray ? anArray : [anArray _javaScriptArrayCopy],
755  argumentArray = concat.call([self _javaScriptArrayCopy], anArray);
756 
757  return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
758 }
759 
760 /*
761 - (CPArray)filteredArrayUsingPredicate:(CPPredicate)aPredicate
762 {
763  var i= 0,
764  count = [self count],
765  array = [CPArray array];
766 
767  for (; i<count; ++i)
768  if (aPredicate.evaluateWithObject(self[i]))
769  array.push(self[i]);
770 
771  return array;
772 }
773 */
774 
780 - (CPArray)subarrayWithRange:(CPRange)aRange
781 {
782  if (!aRange)
783  return [self copy];
784 
785  if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
786  [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"];
787 
788  var index = aRange.location,
789  count = CPMaxRange(aRange),
790  argumentArray = [];
791 
792  for (; index < count; ++index)
793  push.call(argumentArray, [self objectAtIndex:index]);
794 
795  return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
796 }
797 
798 // Sorting arrays
799 /*
800  Not yet described.
801 */
802 - (CPArray)sortedArrayUsingDescriptors:(CPArray)descriptors
803 {
804  var sorted = [self copy];
805 
806  [sorted sortUsingDescriptors:descriptors];
807 
808  return sorted;
809 }
810 
814 - (CPArray)sortedArrayUsingFunction:(Function)aFunction
815 {
816  return [self sortedArrayUsingFunction:aFunction context:nil];
817 }
818 
827 - (CPArray)sortedArrayUsingFunction:(Function)aFunction context:(id)aContext
828 {
829  var sorted = [self copy];
830 
831  [sorted sortUsingFunction:aFunction context:aContext];
832 
833  return sorted;
834 }
835 
840 - (CPArray)sortedArrayUsingSelector:(SEL)aSelector
841 {
842  var sorted = [self copy];
843 
844  [sorted sortUsingSelector:aSelector];
845 
846  return sorted;
847 }
848 
849 // Working with string elements
850 
859 - (CPString)componentsJoinedByString:(CPString)aString
860 {
861  return join.call([self _javaScriptArrayCopy], aString);
862 }
863 
864 // Creating a description of the array
865 
869 - (CPString)description
870 {
871  var index = 0,
872  count = [self count],
873  description = "@[";
874 
875  for (; index < count; ++index)
876  {
877  if (index === 0)
878  description += "\n\t";
879 
880  var object = [self objectAtIndex:index];
881  description += CPDescriptionOfObject(object);
882 
883  if (index !== count - 1)
884  description += ",\n\t";
885  else
886  description += "\n";
887  }
888 
889  return description + "]";
890 }
891 
892 // Collecting paths
900 - (CPArray)pathsMatchingExtensions:(CPArray)filterTypes
901 {
902  var index = 0,
903  count = [self count],
904  array = [];
905 
906  for (; index < count; ++index)
907  if (self[index].isa && [self[index] isKindOfClass:[CPString class]] && [filterTypes containsObject:[self[index] pathExtension]])
908  array.push(self[index]);
909 
910  return array;
911 }
912 
913 // Copying arrays
914 
919 - (id)copy
920 {
921  return [[self class] arrayWithArray:self];
922 }
923 
924 @end
925 
926 @implementation CPArray (CPCoding)
927 
928 - (id)initWithCoder:(CPCoder)aCoder
929 {
930  return [aCoder decodeObjectForKey:@"CP.objects"];
931 }
932 
933 - (void)encodeWithCoder:(CPCoder)aCoder
934 {
935  [aCoder _encodeArrayOfObjects:self forKey:@"CP.objects"];
936 }
937 
938 @end
939 
940 /* @ignore */
941 @implementation _CPArrayEnumerator : CPEnumerator
942 {
943  CPArray _array;
944  int _index;
945 }
946 
947 - (id)initWithArray:(CPArray)anArray
948 {
949  self = [super init];
950 
951  if (self)
952  {
953  _array = anArray;
954  _index = -1;
955  }
956 
957  return self;
958 }
959 
960 - (id)nextObject
961 {
962  if (++_index >= [_array count])
963  return nil;
964 
965  return [_array objectAtIndex:_index];
966 }
967 
968 @end
969 
970 /* @ignore */
971 @implementation _CPReverseArrayEnumerator : CPEnumerator
972 {
973  CPArray _array;
974  int _index;
975 }
976 
977 - (id)initWithArray:(CPArray)anArray
978 {
979  self = [super init];
980 
981  if (self)
982  {
983  _array = anArray;
984  _index = [_array count];
985  }
986 
987  return self;
988 }
989 
990 - (id)nextObject
991 {
992  if (--_index < 0)
993  return nil;
994 
995  return [_array objectAtIndex:_index];
996 }
997 
998 @end
999 
1000 var _CPSharedPlaceholderArray = nil;
1001 @implementation _CPPlaceholderArray : CPArray
1002 {
1003  id __doxygen__;
1004 }
1005 
1006 + (id)alloc
1007 {
1008  if (!_CPSharedPlaceholderArray)
1009  _CPSharedPlaceholderArray = [super alloc];
1010 
1011  return _CPSharedPlaceholderArray;
1012 }
1013 
1014 @end
1015