API 0.9.5
Foundation/CPArray/CPArray.j
Go to the documentation of this file.
00001 /*
00002  * CPArray.j
00003  * Foundation
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 CPEnumerationNormal             = 0;
00026 CPEnumerationConcurrent         = 1 << 0;
00027 CPEnumerationReverse            = 1 << 1;
00028 
00029 CPBinarySearchingFirstEqual     = 1 << 8;
00030 CPBinarySearchingLastEqual      = 1 << 9;
00031 CPBinarySearchingInsertionIndex = 1 << 10;
00032 
00033 var concat = Array.prototype.concat,
00034     join = Array.prototype.join,
00035     push = Array.prototype.push;
00036 
00037 #define FORWARD_TO_CONCRETE_CLASS()\
00038     if (self === _CPSharedPlaceholderArray)\
00039     {\
00040         arguments[0] = [_CPJavaScriptArray alloc];\
00041         return objj_msgSend.apply(this, arguments);\
00042     }\
00043     return [super init];
00044 
00056 @implementation CPArray : CPObject
00057 {
00058     id __doxygen__;
00059 }
00060 
00064 + (id)alloc
00065 {
00066     if (self === CPArray || self === CPMutableArray)
00067         return [_CPPlaceholderArray alloc];
00068 
00069     return [super alloc];
00070 }
00071 
00075 + (id)array
00076 {
00077     return [[self alloc] init];
00078 }
00079 
00085 + (id)arrayWithArray:(CPArray)anArray
00086 {
00087     return [[self alloc] initWithArray:anArray];
00088 }
00089 
00095 + (id)arrayWithObject:(id)anObject
00096 {
00097     return [[self alloc] initWithObjects:anObject];
00098 }
00099 
00105 + (id)arrayWithObjects:(id)anObject, ...
00106 {
00107     arguments[0] = [self alloc];
00108     arguments[1] = @selector(initWithObjects:);
00109 
00110     return objj_msgSend.apply(this, arguments);
00111 }
00112 
00119 + (id)arrayWithObjects:(id)objects count:(unsigned)aCount
00120 {
00121     return [[self alloc] initWithObjects:objects count:aCount];
00122 }
00123 
00128 - (id)init
00129 {
00130     FORWARD_TO_CONCRETE_CLASS();
00131 }
00132 
00133 // Creating an Array
00139 - (id)initWithArray:(CPArray)anArray
00140 {
00141     FORWARD_TO_CONCRETE_CLASS();
00142 }
00143 
00152 - (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems
00153 {
00154     FORWARD_TO_CONCRETE_CLASS();
00155 }
00156 
00160 - (id)initWithObjects:(id)anObject, ...
00161 {
00162     FORWARD_TO_CONCRETE_CLASS();
00163 }
00164 
00171 - (id)initWithObjects:(id)objects count:(unsigned)aCount
00172 {
00173     FORWARD_TO_CONCRETE_CLASS();
00174 }
00175 
00176 // FIXME: This should be defined in CPMutableArray, not here.
00177 - (id)initWithCapacity:(unsigned)aCapacity
00178 {
00179     FORWARD_TO_CONCRETE_CLASS();
00180 }
00181 
00182 // Querying an array
00187 - (BOOL)containsObject:(id)anObject
00188 {
00189     return [self indexOfObject:anObject] !== CPNotFound;
00190 }
00191 
00192 - (BOOL)containsObjectIdenticalTo:(id)anObject
00193 {
00194     return [self indexOfObjectIdenticalTo:anObject] !== CPNotFound;
00195 }
00196 
00200 - (int)count
00201 {
00202     _CPRaiseInvalidAbstractInvocation(self, _cmd);
00203 }
00204 
00208 - (id)firstObject
00209 {
00210     var count = [self count];
00211 
00212     if (count > 0)
00213         return [self objectAtIndex:0];
00214 
00215     return nil;
00216 }
00217 
00221 - (id)lastObject
00222 {
00223     var count = [self count];
00224 
00225     if (count <= 0)
00226         return nil;
00227 
00228     return [self objectAtIndex:count - 1];
00229 }
00230 
00235 - (id)objectAtIndex:(int)anIndex
00236 {
00237     _CPRaiseInvalidAbstractInvocation(self, _cmd);
00238 }
00239 
00245 - (CPArray)objectsAtIndexes:(CPIndexSet)indexes
00246 {
00247     var index = CPNotFound,
00248         objects = [];
00249 
00250     while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound)
00251         objects.push([self objectAtIndex:index]);
00252 
00253     return objects;
00254 }
00255 
00261 - (CPEnumerator)objectEnumerator
00262 {
00263     return [[_CPArrayEnumerator alloc] initWithArray:self];
00264 }
00265 
00271 - (CPEnumerator)reverseObjectEnumerator
00272 {
00273     return [[_CPReverseArrayEnumerator alloc] initWithArray:self];
00274 }
00275 
00283 - (CPUInteger)indexOfObject:(id)anObject
00284 {
00285     return [self indexOfObject:anObject inRange:nil];
00286 }
00287 
00296 - (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange
00297 {
00298     // Only use isEqual: if our object is a CPObject.
00299     if (anObject && anObject.isa)
00300     {
00301         var index = aRange ? aRange.location : 0,
00302             count = aRange ? CPMaxRange(aRange) : [self count];
00303 
00304         for (; index < count; ++index)
00305             if ([[self objectAtIndex:index] isEqual:anObject])
00306                 return index;
00307 
00308         return CPNotFound;
00309     }
00310 
00311     return [self indexOfObjectIdenticalTo:anObject inRange:aRange];
00312 }
00313 
00319 - (CPUInteger)indexOfObjectIdenticalTo:(id)anObject
00320 {
00321     return [self indexOfObjectIdenticalTo:anObject inRange:nil];
00322 }
00323 
00332 - (CPUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange
00333 {
00334     var index = aRange ? aRange.location : 0,
00335         count = aRange ? CPMaxRange(aRange) : [self count];
00336 
00337     for (; index < count; ++index)
00338         if ([self objectAtIndex:index] === anObject)
00339             return index;
00340 
00341     return CPNotFound;
00342 }
00343 
00353 - (unsigned)indexOfObjectPassingTest:(Function)aPredicate
00354 {
00355     return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:undefined];
00356 }
00357 
00369 - (unsigned)indexOfObjectPassingTest:(Function)aPredicate context:(id)aContext
00370 {
00371     return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:aContext];
00372 }
00373 
00385 - (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function)aPredicate
00386 {
00387     return [self indexOfObjectWithOptions:options passingTest:aPredicate context:undefined];
00388 }
00389 
00403 - (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function)aPredicate context:(id)aContext
00404 {
00405     // We don't use an enumerator because they return nil to indicate end of enumeration,
00406     // but nil may actually be the value we are looking for, so we have to loop over the array.
00407     if (options & CPEnumerationReverse)
00408     {
00409         var index = [self count] - 1,
00410             stop = -1,
00411             increment = -1;
00412     }
00413     else
00414     {
00415         var index = 0,
00416             stop = [self count],
00417             increment = 1;
00418     }
00419 
00420     for (; index !== stop; index += increment)
00421         if (aPredicate([self objectAtIndex:index], index, aContext))
00422             return index;
00423 
00424     return CPNotFound;
00425 }
00426 
00427 - (CPUInteger)indexOfObject:(id)anObject
00428               inSortedRange:(CPRange)aRange
00429                     options:(CPBinarySearchingOptions)options
00430             usingComparator:(Function)aComparator
00431 {
00432     // FIXME: comparator is not a function
00433     if (!aComparator)
00434         _CPRaiseInvalidArgumentException(self, _cmd, "comparator is nil");
00435 
00436     if ((options & CPBinarySearchingFirstEqual) && (options & CPBinarySearchingLastEqual))
00437         _CPRaiseInvalidArgumentException(self, _cmd,
00438             "both CPBinarySearchingFirstEqual and CPBinarySearchingLastEqual options cannot be specified");
00439 
00440     var count = [self count];
00441 
00442     if (count <= 0)
00443         return (options & CPBinarySearchingInsertionIndex) ? 0 : CPNotFound;
00444 
00445     var first = aRange ? aRange.location : 0,
00446         last = (aRange ? CPMaxRange(aRange) : [self count]) - 1;
00447 
00448     if (first < 0)
00449         _CPRaiseRangeException(self, _cmd, first, count);
00450 
00451     if (last >= count)
00452         _CPRaiseRangeException(self, _cmd, last, count);
00453 
00454     while (first <= last)
00455     {
00456         var middle = FLOOR((first + last) / 2),
00457             result = aComparator(anObject, [self objectAtIndex:middle]);
00458 
00459         if (result > 0)
00460             first = middle + 1;
00461 
00462         else if (result < 0)
00463             last = middle - 1;
00464 
00465         else
00466         {
00467             if (options & CPBinarySearchingFirstEqual)
00468                 while (middle > first && aComparator(anObject, [self objectAtIndex:middle - 1]) === CPOrderedSame)
00469                     --middle;
00470 
00471             else if (options & CPBinarySearchingLastEqual)
00472             {
00473                 while (middle < last && aComparator(anObject, [self objectAtIndex:middle + 1]) === CPOrderedSame)
00474                     ++middle;
00475 
00476                 if (options & CPBinarySearchingInsertionIndex)
00477                     ++middle;
00478             }
00479 
00480             return middle;
00481         }
00482     }
00483 
00484     if (options & CPBinarySearchingInsertionIndex)
00485         return MAX(first, 0);
00486 
00487     return CPNotFound;
00488 }
00489 
00490 // Sending messages to elements
00496 - (void)makeObjectsPerformSelector:(SEL)aSelector
00497 {
00498     [self makeObjectsPerformSelector:aSelector withObjects:nil];
00499 }
00500 
00507 - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject
00508 {
00509     return [self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
00510 }
00511 
00512 - (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
00513 {
00514     if (!aSelector)
00515         [CPException raise:CPInvalidArgumentException
00516                     reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"];
00517 
00518     var index = 0,
00519         count = [self count];
00520 
00521     if ([objects count])
00522     {
00523         argumentsArray = [[nil, aSelector] arrayByAddingObjectsFromArray:objects];
00524 
00525         for (; index < count; ++index)
00526         {
00527             argumentsArray[0] = [self objectAtIndex:index];
00528             objj_msgSend.apply(this, argumentsArray);
00529         }
00530     }
00531 
00532     else
00533         for (; index < count; ++index)
00534             objj_msgSend([self objectAtIndex:index], aSelector);
00535 }
00536 
00537 - (void)enumerateObjectsUsingBlock:(Function)aFunction
00538 {
00539     var index = 0,
00540         count = [self count];
00541 
00542     for (; index < count; ++index)
00543         aFunction([self objectAtIndex:index], index);
00544 }
00545 
00546 // Comparing arrays
00552 - (id)firstObjectCommonWithArray:(CPArray)anArray
00553 {
00554     var count = [self count];
00555 
00556     if (![anArray count] || !count)
00557         return nil;
00558 
00559     var index = 0;
00560 
00561     for (; index < count; ++index)
00562     {
00563         var object = [self objectAtIndex:index];
00564 
00565         if ([anArray containsObject:object])
00566             return object;
00567     }
00568 
00569     return nil;
00570 }
00571 
00575 - (BOOL)isEqualToArray:(id)anArray
00576 {
00577     if (self === anArray)
00578         return YES;
00579 
00580     if (![anArray isKindOfClass:CPArray])
00581         return NO;
00582 
00583     var count = [self count],
00584         otherCount = [anArray count];
00585 
00586     if (anArray === nil || count !== otherCount)
00587         return NO;
00588 
00589     var index = 0;
00590 
00591     for (; index < count; ++index)
00592     {
00593         var lhs = [self objectAtIndex:index],
00594             rhs = [anArray objectAtIndex:index];
00595 
00596         // If they're not equal, and either doesn't have an isa, or they're !isEqual (not isEqual)
00597         if (lhs !== rhs && (lhs && !lhs.isa || rhs && !rhs.isa || ![lhs isEqual:rhs]))
00598             return NO;
00599     }
00600 
00601     return YES;
00602 }
00603 
00604 - (BOOL)isEqual:(id)anObject
00605 {
00606     return (self === anObject) || [self isEqualToArray:anObject];
00607 }
00608 
00609 - (Array)_javaScriptArrayCopy
00610 {
00611     var index = 0,
00612         count = [self count],
00613         copy = [];
00614 
00615     for (; index < count; ++index)
00616         push.call(copy, [self objectAtIndex:index]);
00617 
00618     return copy;
00619 }
00620 
00621 // Deriving new arrays
00628 - (CPArray)arrayByAddingObject:(id)anObject
00629 {
00630     var argumentArray = [self _javaScriptArrayCopy];
00631 
00632     // We push instead of concat,because concat flattens arrays, so if the object
00633     // passed in is an array, we end up with its contents added instead of itself.
00634     push.call(argumentArray, anObject);
00635 
00636     return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
00637 }
00638 
00643 - (CPArray)arrayByAddingObjectsFromArray:(CPArray)anArray
00644 {
00645     if (!anArray)
00646         return [self copy];
00647 
00648     var anArray = anArray.isa === _CPJavaScriptArray ? anArray : [anArray _javaScriptArrayCopy],
00649         argumentArray = concat.call([self _javaScriptArrayCopy], anArray);
00650 
00651     return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
00652 }
00653 
00654 /*
00655 - (CPArray)filteredArrayUsingPredicate:(CPPredicate)aPredicate
00656 {
00657     var i= 0,
00658         count = [self count],
00659         array = [CPArray array];
00660 
00661     for (; i<count; ++i)
00662         if (aPredicate.evaluateWithObject(self[i]))
00663             array.push(self[i]);
00664 
00665     return array;
00666 }
00667 */
00668 
00674 - (CPArray)subarrayWithRange:(CPRange)aRange
00675 {
00676     if (!aRange)
00677         return [self copy];
00678 
00679     if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
00680         [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"];
00681 
00682     var index = aRange.location,
00683         count = CPMaxRange(aRange),
00684         argumentArray = [];
00685 
00686     for (; index < count; ++index)
00687         push.call(argumentArray, [self objectAtIndex:index]);
00688 
00689     return objj_msgSend([self class], @selector(arrayWithArray:), argumentArray);
00690 }
00691 
00692 // Sorting arrays
00693 /*
00694     Not yet described.
00695 */
00696 - (CPArray)sortedArrayUsingDescriptors:(CPArray)descriptors
00697 {
00698     var sorted = [self copy];
00699 
00700     [sorted sortUsingDescriptors:descriptors];
00701 
00702     return sorted;
00703 }
00704 
00708 - (CPArray)sortedArrayUsingFunction:(Function)aFunction
00709 {
00710     return [self sortedArrayUsingFunction:aFunction context:nil];
00711 }
00712 
00721 - (CPArray)sortedArrayUsingFunction:(Function)aFunction context:(id)aContext
00722 {
00723     var sorted = [self copy];
00724 
00725     [sorted sortUsingFunction:aFunction context:aContext];
00726 
00727     return sorted;
00728 }
00729 
00734 - (CPArray)sortedArrayUsingSelector:(SEL)aSelector
00735 {
00736     var sorted = [self copy];
00737 
00738     [sorted sortUsingSelector:aSelector];
00739 
00740     return sorted;
00741 }
00742 
00743 // Working with string elements
00744 
00753 - (CPString)componentsJoinedByString:(CPString)aString
00754 {
00755     return join.call([self _javaScriptArrayCopy], aString);
00756 }
00757 
00758 // Creating a description of the array
00759 
00763 - (CPString)description
00764 {
00765     var index = 0,
00766         count = [self count],
00767         description = '(';
00768 
00769     for (; index < count; ++index)
00770     {
00771         if (index === 0)
00772             description += '\n';
00773 
00774         var object = [self objectAtIndex:index],
00775             objectDescription = object && object.isa ? [object description] : String(object);
00776 
00777         description += "\t" + objectDescription.split('\n').join("\n\t");
00778 
00779         if (index !== count - 1)
00780             description += ", ";
00781 
00782         description += '\n';
00783     }
00784 
00785     return description + ')';
00786 }
00787 
00788 // Collecting paths
00796 - (CPArray)pathsMatchingExtensions:(CPArray)filterTypes
00797 {
00798     var index = 0,
00799         count = [self count],
00800         array = [];
00801 
00802     for (; index < count; ++index)
00803         if (self[index].isa && [self[index] isKindOfClass:[CPString class]] && [filterTypes containsObject:[self[index] pathExtension]])
00804             array.push(self[index]);
00805 
00806     return array;
00807 }
00808 
00809 // Copying arrays
00810 
00815 - (id)copy
00816 {
00817     return [[self class] arrayWithArray:self];
00818 }
00819 
00820 @end
00821 
00822 @implementation CPArray (CPCoding)
00823 
00824 - (id)initWithCoder:(CPCoder)aCoder
00825 {
00826     return [aCoder decodeObjectForKey:@"CP.objects"];
00827 }
00828 
00829 - (void)encodeWithCoder:(CPCoder)aCoder
00830 {
00831     [aCoder _encodeArrayOfObjects:self forKey:@"CP.objects"];
00832 }
00833 
00834 @end
00835 
00836 /* @ignore */
00837 @implementation _CPArrayEnumerator : CPEnumerator
00838 {
00839     CPArray _array;
00840     int     _index;
00841 }
00842 
00843 - (id)initWithArray:(CPArray)anArray
00844 {
00845     self = [super init];
00846 
00847     if (self)
00848     {
00849         _array = anArray;
00850         _index = -1;
00851     }
00852 
00853     return self;
00854 }
00855 
00856 - (id)nextObject
00857 {
00858     if (++_index >= [_array count])
00859         return nil;
00860 
00861     return [_array objectAtIndex:_index];
00862 }
00863 
00864 @end
00865 
00866 /* @ignore */
00867 @implementation _CPReverseArrayEnumerator : CPEnumerator
00868 {
00869     CPArray _array;
00870     int     _index;
00871 }
00872 
00873 - (id)initWithArray:(CPArray)anArray
00874 {
00875     self = [super init];
00876 
00877     if (self)
00878     {
00879         _array = anArray;
00880         _index = [_array count];
00881     }
00882 
00883     return self;
00884 }
00885 
00886 - (id)nextObject
00887 {
00888     if (--_index < 0)
00889         return nil;
00890 
00891     return [_array objectAtIndex:_index];
00892 }
00893 
00894 @end
00895 
00896 var _CPSharedPlaceholderArray   = nil;
00897 @implementation _CPPlaceholderArray : CPArray
00898 {
00899     id __doxygen__;
00900 }
00901 
00902 + (id)alloc
00903 {
00904     if (!_CPSharedPlaceholderArray)
00905         _CPSharedPlaceholderArray = [super alloc];
00906 
00907     return _CPSharedPlaceholderArray;
00908 }
00909 
00910 @end
00911 
 All Classes Files Functions Variables Defines