diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 9d20a03f2..ec4e0370a 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -908,7 +908,7 @@ CPRunContinuesResponse = -1002; return; } - if ([args class] != CPArray) + if (![args isKindOfClass:CPArray]) args = [CPArray arrayWithObject:args]; _args = args; diff --git a/AppKit/CPObjectController.j b/AppKit/CPObjectController.j index 0027f423a..4d764a440 100644 --- a/AppKit/CPObjectController.j +++ b/AppKit/CPObjectController.j @@ -341,7 +341,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo @end -@implementation _CPObservableArray : CPMutableArray +@implementation _CPObservableArray : _CPJavaScriptArray { CPArray _observationProxies; } diff --git a/Foundation/CPArray.j b/Foundation/CPArray/CPArray.j similarity index 54% rename from Foundation/CPArray.j rename to Foundation/CPArray/CPArray.j index f0908805d..6103a3416 100755 --- a/Foundation/CPArray.j +++ b/Foundation/CPArray/CPArray.j @@ -30,65 +30,13 @@ CPEnumerationNormal = 0; CPEnumerationConcurrent = 1 << 0; CPEnumerationReverse = 1 << 1; -/* @ignore */ -@implementation _CPArrayEnumerator : CPEnumerator -{ - CPArray _array; - int _index; -} - -- (id)initWithArray:(CPArray)anArray -{ - self = [super init]; - - if (self) - { - _array = anArray; - _index = -1; - } - - return self; -} - -- (id)nextObject -{ - if (++_index >= [_array count]) - return nil; - - return [_array objectAtIndex:_index]; -} - -@end - -/* @ignore */ -@implementation _CPReverseArrayEnumerator : CPEnumerator -{ - CPArray _array; - int _index; -} - -- (id)initWithArray:(CPArray)anArray -{ - self = [super init]; - - if (self) - { - _array = anArray; - _index = [_array count]; - } - - return self; -} - -- (id)nextObject -{ - if (--_index < 0) - return nil; - - return [_array objectAtIndex:_index]; -} - -@end +#define FORWARD_TO_CONCRETE_CLASS()\ + if (self === _CPSharedPlaceholderArray)\ + {\ + arguments[0] = [_CPJavaScriptArray alloc];\ + return objj_msgSend.apply(this, arguments);\ + }\ + return [super init]; /*! @class CPArray @@ -108,9 +56,10 @@ CPEnumerationReverse = 1 << 1; */ + (id)alloc { - var result = []; - result.isa = [self class]; - return result; + if (self === CPArray || self === CPMutableArray) + return [_CPPlaceholderArray alloc]; + + return [super alloc]; } /*! @@ -148,14 +97,10 @@ CPEnumerationReverse = 1 << 1; */ + (id)arrayWithObjects:(id)anObject, ... { - var i = 2, - array = [[self alloc] init], - count = arguments.length; + arguments[0] = [self alloc]; + arguments[1] = @selector(initWithObjects:); - for (; i < count; ++i) - array.push(arguments[i]); - - return array; + return objj_msgSend.apply(this, arguments); } /*! @@ -175,7 +120,7 @@ CPEnumerationReverse = 1 << 1; */ - (id)init { - return self; + FORWARD_TO_CONCRETE_CLASS(); } // Creating an Array @@ -186,12 +131,7 @@ CPEnumerationReverse = 1 << 1; */ - (id)initWithArray:(CPArray)anArray { - self = [super init]; - - if (self) - [self setArray:anArray]; - - return self; + FORWARD_TO_CONCRETE_CLASS(); } /*! @@ -202,44 +142,20 @@ CPEnumerationReverse = 1 << 1; returned object will be added to the receiver. Otherwise, no copying will be performed. @return the initialized array of objects */ -- (id)initWithArray:(CPArray)anArray copyItems:(BOOL)copyItems +- (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems { if (!copyItems) return [self initWithArray:anArray]; - self = [super init]; - - if (self) - { - var index = 0, - count = [anArray count]; - - for (; index < count; ++index) - { - if (anArray[index].isa) - self[index] = [anArray[index] copy]; - // Do a deep/shallow copy? - else - self[index] = anArray[index]; - } - } - - return self; + FORWARD_TO_CONCRETE_CLASS(); } /*! initializes an array with the contents of anArray */ -- (id)initWithObjects:(Array)anArray, ... +- (id)initWithObjects:(id)anObject, ... { - // The arguments array contains self and _cmd, so the first object is at position 2. - var i = 2, - count = arguments.length; - - for (; i < count; ++i) - push(arguments[i]); - - return self; + FORWARD_TO_CONCRETE_CLASS(); } /*! @@ -250,17 +166,7 @@ CPEnumerationReverse = 1 << 1; */ - (id)initWithObjects:(id)objects count:(unsigned)aCount { - self = [super init]; - - if (self) - { - var index = 0; - - for (; index < aCount; ++index) - push(objects[index]); - } - - return self; + FORWARD_TO_CONCRETE_CLASS(); } // Querying an array @@ -270,7 +176,7 @@ CPEnumerationReverse = 1 << 1; */ - (BOOL)containsObject:(id)anObject { - return [self indexOfObject:anObject] != CPNotFound; + return [self indexOfObject:anObject] !== CPNotFound; } /*! @@ -278,7 +184,65 @@ CPEnumerationReverse = 1 << 1; */ - (int)count { - return length; + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Returns the last object in the array. If the array is empty, returns \c nil/ +*/ +- (id)lastObject +{ + var count = [self count]; + + if (count <= 0) + return nil; + + return [self objectAtIndex:count - 1]; +} + +/*! + Returns the object at index \c anIndex. + @throws CPRangeException if \c anIndex is out of bounds +*/ +- (id)objectAtIndex:(int)anIndex +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Returns the objects at \c indexes in a new CPArray. + @param indexes the set of indices + @throws CPRangeException if any of the indices is greater than or equal to the length of the array +*/ +- (CPArray)objectsAtIndexes:(CPIndexSet)indexes +{ + var index = CPNotFound, + objects = []; + + while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound) + objects.push([self objectAtIndex:index]); + + return objects; +} + +/*! + Returns an enumerator describing the array sequentially + from the first to the last element. You should not modify + the array during enumeration. +*/ +- (CPEnumerator)objectEnumerator +{ + return [[_CPArrayEnumerator alloc] initWithArray:self]; +} + +/*! + Returns an enumerator describing the array sequentially + from the last to the first element. You should not modify + the array during enumeration. +*/ +- (CPEnumerator)reverseObjectEnumerator +{ + return [[_CPReverseArrayEnumerator alloc] initWithArray:self]; } /*! @@ -288,29 +252,9 @@ CPEnumerationReverse = 1 << 1; a match using \c -isEqual:, then \c ===. @param anObject the object to search for */ -- (int)indexOfObject:(id)anObject +- (CPUInteger)indexOfObject:(id)anObject { - var i = 0, - count = length; - - // Only use -isEqual: if our object is a CPObject. - if (anObject && anObject.isa) - { - for (; i < count; ++i) - if ([self[i] isEqual:anObject]) - return i; - } - // If indexOf exists, use it since it's probably - // faster than anything we can implement. - else if (self.indexOf) - return indexOf(anObject); - // Last resort, do a straight forward linear O(N) search. - else - for (; i < count; ++i) - if (self[i] === anObject) - return i; - - return CPNotFound; + return [self indexOfObject:anObject inRange:nil]; } /*! @@ -321,25 +265,22 @@ CPEnumerationReverse = 1 << 1; @param aRange the range to search within @return the index of the object, or \c CPNotFound if it was not found. */ -- (int)indexOfObject:(id)anObject inRange:(CPRange)aRange +- (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange { - var i = aRange.location, - count = MIN(CPMaxRange(aRange), length); - // Only use isEqual: if our object is a CPObject. if (anObject && anObject.isa) { - for (; i < count; ++i) - if ([self[i] isEqual:anObject]) - return i; - } - // Last resort, do a straight forward linear O(N) search. - else - for (; i < count; ++i) - if (self[i] === anObject) - return i; + var index = aRange ? aRange.location : 0, + count = aRange ? CPMaxRange(aRange) : [self count]; - return CPNotFound; + for (; index < count; ++index) + if ([[self objectAtIndex:index] isEqual:anObject]) + return index; + + return CPNotFound; + } + + return [self indexOfObjectIdenticalTo:anObject inRange:aRange]; } /*! @@ -347,25 +288,9 @@ CPEnumerationReverse = 1 << 1; @param anObject the object to search for @return the index of the object in the array. \c CPNotFound if the object is not in the array. */ -- (int)indexOfObjectIdenticalTo:(id)anObject +- (CPUInteger)indexOfObjectIdenticalTo:(id)anObject { - // If indexOf exists, use it since it's probably - // faster than anything we can implement. - if (self.indexOf) - return indexOf(anObject); - - // Last resort, do a straight forward linear O(N) search. - else - { - var index = 0, - count = length; - - for (; index < count; ++index) - if (self[index] === anObject) - return index; - } - - return CPNotFound; + return [self indexOfObjectIdenticalTo:anObject inRange:nil]; } /*! @@ -376,28 +301,14 @@ CPEnumerationReverse = 1 << 1; @param aRange the range to search within @return the index of the object, or \c CPNotFound if it was not found. */ -- (int)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange +- (CPUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange { - // If indexOf exists, use it since it's probably - // faster than anything we can implement. - if (self.indexOf) - { - var index = indexOf(anObject, aRange.location); + var index = aRange ? aRange.location : 0, + count = aRange ? CPMaxRange(aRange) : [self count]; - if (CPLocationInRange(index, aRange)) + for (; index < count; ++index) + if ([self objectAtIndex:index] === anObject) return index; - } - - // Last resort, do a straight forward linear O(N) search. - else - { - var index = aRange.location, - count = MIN(CPMaxRange(aRange), length); - - for (; index < count; ++index) - if (self[index] == anObject) - return index; - } return CPNotFound; } @@ -411,9 +322,9 @@ CPEnumerationReverse = 1 << 1; or nil to stop the search, which will return CPNotFound to the sender. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ -- (unsigned)indexOfObjectPassingTest:(Function)predicate +- (unsigned)indexOfObjectPassingTest:(Function)aPredicate { - return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate context:undefined]; + return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:undefined]; } /*! @@ -427,14 +338,14 @@ CPEnumerationReverse = 1 << 1; @param context An object that contains context information you want passed to the predicate function. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ -- (unsigned)indexOfObjectPassingTest:(Function)predicate context:(id)aContext +- (unsigned)indexOfObjectPassingTest:(Function)aPredicate context:(id)aContext { - return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate context:aContext]; + return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:aPredicate context:aContext]; } /*! Returns the index of the first object in the receiver that passes a test in a given Javascript function. - @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards + @param options Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards or CPEnumerationReverse to search in reverse. @param predicate The function to apply to elements of the array. The function receives two arguments: object The element in the array. @@ -443,14 +354,14 @@ CPEnumerationReverse = 1 << 1; or nil to stop the search, which will return CPNotFound to the sender. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ -- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)opts passingTest:(Function)predicate +- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function)aPredicate { - return [self indexOfObjectWithOptions:opts passingTest:predicate context:undefined]; + return [self indexOfObjectWithOptions:options passingTest:aPredicate context:undefined]; } /*! Returns the index of the first object in the receiver that passes a test in a given Javascript function. - @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards + @param options Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards or CPEnumerationReverse to search in reverse. @param predicate The function to apply to elements of the array. The function receives two arguments: object The element in the array. @@ -461,37 +372,26 @@ CPEnumerationReverse = 1 << 1; @param context An object that contains context information you want passed to the predicate function. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ -- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)opts passingTest:(Function)predicate context:(id)aContext +- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)options passingTest:(Function)aPredicate context:(id)aContext { // We don't use an enumerator because they return nil to indicate end of enumeration, // but nil may actually be the value we are looking for, so we have to loop over the array. - - var start, - stop, - increment; - - if (opts & CPEnumerationReverse) + if (options & CPEnumerationReverse) { - start = [self count] - 1; - stop = -1; - increment = -1; + var index = [self count] - 1, + stop = -1, + increment = -1; } else { - start = 0; - stop = [self count]; - increment = 1; + var index = 0, + stop = [self count], + increment = 1; } - for (var i = start; i != stop; i += increment) - { - var result = predicate([self objectAtIndex:i], i, aContext); - - if (typeof result === 'boolean' && result) - return i; - else if (typeof result === 'object' && result == nil) - return CPNotFound; - } + for (; index !== stop; index += increment) + if (aPredicate([self objectAtIndex:index], index, aContext)) + return index; return CPNotFound; } @@ -506,7 +406,10 @@ CPEnumerationReverse = 1 << 1; */ - (unsigned)indexOfObject:(id)anObject sortedBySelector:(SEL)aSelector { - return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) { objj_msgSend(lhs, aSelector, rhs); }]; + return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) + { + return objj_msgSend(lhs, aSelector, rhs); + }]; } /*! @@ -539,44 +442,48 @@ CPEnumerationReverse = 1 << 1; @param aContext a context object that will be passed to the sort function @return the index of the object, or \c CPNotFound if it was not found. */ -- (unsigned)indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext -{ - var result = [self _indexOfObject:anObject sortedByFunction:aFunction context:aContext]; - return result >= 0 ? result : CPNotFound; -} - -- (unsigned)_indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext +- (CPUInteger)indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext { if (!aFunction) return CPNotFound; - if (length === 0) - return -1; + var count = [self count]; + + if (count <= 0) + return CPNotFound; var mid, c, first = 0, - last = length - 1; + last = count - 1; while (first <= last) { mid = FLOOR((first + last) / 2); - c = aFunction(anObject, self[mid], aContext); + c = aFunction(anObject, [self objectAtIndex:mid], aContext); if (c > 0) first = mid + 1; + else if (c < 0) last = mid - 1; + else { - while (mid < length - 1 && aFunction(anObject, self[mid + 1], aContext) == CPOrderedSame) + while (mid < count - 1 && + aFunction(anObject, [self objectAtIndex:mid + 1], aContext) === CPOrderedSame) mid++; return mid; } } - return -first - 1; + var result = -first - 1; + + if (result < 0) + return CPNotFound; + + return result; } /*! @@ -593,12 +500,10 @@ CPEnumerationReverse = 1 << 1; return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) { - var i = 0, + var index = 0, result = CPOrderedSame; - while (i < count) - if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) - return result; + while (index < count && (result = [[descriptors objectAtIndex:index++] compareObject:lhs withObject:rhs]) === CPOrderedSame); return result; }]; @@ -632,67 +537,6 @@ CPEnumerationReverse = 1 << 1; return index; } -/*! - Returns the last object in the array. If the array is empty, returns \c nil/ -*/ -- (id)lastObject -{ - var count = [self count]; - - if (!count) - return nil; - - return self[count - 1]; -} - -/*! - Returns the object at index \c anIndex. - @throws CPRangeException if \c anIndex is out of bounds -*/ -- (id)objectAtIndex:(int)anIndex -{ - if (anIndex >= length || anIndex < 0) - [CPException raise:CPRangeException reason:@"index (" + anIndex + @") beyond bounds (" + length + @")"]; - - return self[anIndex]; -} - -/*! - Returns the objects at \c indexes in a new CPArray. - @param indexes the set of indices - @throws CPRangeException if any of the indices is greater than or equal to the length of the array -*/ -- (CPArray)objectsAtIndexes:(CPIndexSet)indexes -{ - var index = CPNotFound, - objects = []; - - while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound) - [objects addObject:[self objectAtIndex:index]]; - - return objects; -} - -/*! - Returns an enumerator describing the array sequentially - from the first to the last element. You should not modify - the array during enumeration. -*/ -- (CPEnumerator)objectEnumerator -{ - return [[_CPArrayEnumerator alloc] initWithArray:self]; -} - -/*! - Returns an enumerator describing the array sequentially - from the last to the first element. You should not modify - the array during enumeration. -*/ -- (CPEnumerator)reverseObjectEnumerator -{ - return [[_CPReverseArrayEnumerator alloc] initWithArray:self]; -} - // Sending messages to elements /*! Sends each element in the array a message. @@ -701,14 +545,7 @@ CPEnumerationReverse = 1 << 1; */ - (void)makeObjectsPerformSelector:(SEL)aSelector { - if (!aSelector) - [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector: 'aSelector' can't be nil"]; - - var index = 0, - count = length; - - for (; index < count; ++index) - objj_msgSend(self[index], aSelector); + [self makeObjectsPerformSelector:aSelector withObjects:nil]; } /*! @@ -719,30 +556,32 @@ CPEnumerationReverse = 1 << 1; */ - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject { - if (!aSelector) - [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector:withObject 'aSelector' can't be nil"]; - - var index = 0, - count = length; - - for (; index < count; ++index) - objj_msgSend(self[index], aSelector, anObject); + return [self makeObjectsPerformSelector:aSelector withObjects:[anObject]]; } - (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects { if (!aSelector) - [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"]; + [CPException raise:CPInvalidArgumentException + reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"]; var index = 0, - count = length, - argumentsArray = [nil, aSelector].concat(objects || []); + count = [self count]; - for (; index < count; ++index) + if ([objects count]) { - argumentsArray[0] = self[index]; - objj_msgSend.apply(this, argumentsArray); + argumentsArray = [[nil, aSelector] arrayByAddingObjectsFromArray:objects]; + + for (; index < count; ++index) + { + argumentsArray[0] = [self objectAtIndex:index]; + objj_msgSend.apply(this, argumentsArray); + } } + + else + for (; index < count; ++index) + objj_msgSend([self objectAtIndex:index], aSelector); } @@ -754,15 +593,20 @@ CPEnumerationReverse = 1 << 1; */ - (id)firstObjectCommonWithArray:(CPArray)anArray { - if (![anArray count] || ![self count]) + var count = [self count]; + + if (![anArray count] || !count) return nil; - var i = 0, - count = [self count]; + var index = 0; - for (; i < count; ++i) - if ([anArray containsObject:self[i]]) - return self[i]; + for (; index < count; ++index) + { + var object = [self objectAtIndex:index]; + + if ([anArray containsObject:object]) + return object; + } return nil; } @@ -775,16 +619,21 @@ CPEnumerationReverse = 1 << 1; if (self === anArray) return YES; - if (anArray === nil || length !== anArray.length) + if (![anArray isKindOfClass:CPArray]) return NO; - var index = 0, - count = [self count]; + var count = [self count], + otherCount = [anArray count]; + + if (anArray === nil || count !== otherCount) + return NO; + + var index = 0; for (; index < count; ++index) { - var lhs = self[index], - rhs = anArray[index]; + var lhs = [self objectAtIndex:index], + rhs = [anArray objectAtIndex:index]; // If they're not equal, and either doesn't have an isa, or they're !isEqual (not isEqual) if (lhs !== rhs && (lhs && !lhs.isa || rhs && !rhs.isa || ![lhs isEqual:rhs])) @@ -796,13 +645,7 @@ CPEnumerationReverse = 1 << 1; - (BOOL)isEqual:(id)anObject { - if (self === anObject) - return YES; - - if (![anObject isKindOfClass:[CPArray class]]) - return NO; - - return [self isEqualToArray:anObject]; + return (self === anObject) || [self isEqualToArray:anObject]; } // Deriving new arrays @@ -848,10 +691,7 @@ CPEnumerationReverse = 1 << 1; */ - (CPArray)subarrayWithRange:(CPRange)aRange { - if (aRange.location < 0 || CPMaxRange(aRange) > length) - [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"]; - - return slice(aRange.location, CPMaxRange(aRange)); + _CPRaiseInvalidAbstractInvocation(self, _cmd); } // Sorting arrays @@ -917,9 +757,19 @@ CPEnumerationReverse = 1 << 1; */ - (CPString)componentsJoinedByString:(CPString)aString { - // Objective-J objects get "description" called on them automatically when coerced to strings - // (see "objj_object.prototype.toString" at bottom of CPObject.j) - return join(aString); + var index = 0, + count = [self count], + components = []; + + for (; index < count; ++index) + { + components.push([self objectAtIndex:index]); + + if (index < count - 1) + components.push(aString); + } + + return components.join(""); } // Creating a description of the array @@ -986,392 +836,6 @@ CPEnumerationReverse = 1 << 1; @end -@implementation CPArray (CPMutableArray) - -// Creating arrays -/*! - Creates an array able to store at least \c aCapacity - items. Because CPArray is backed by JavaScript arrays, - this method ends up simply returning a regular array. -*/ -+ (CPArray)arrayWithCapacity:(unsigned)aCapacity -{ - return [[self alloc] initWithCapacity:aCapacity]; -} - -/*! - Initializes an array able to store at least \c aCapacity items. Because CPArray - is backed by JavaScript arrays, this method ends up simply returning a regular array. -*/ -- (id)initWithCapacity:(unsigned)aCapacity -{ - return self; -} - -// Adding and replacing objects -/*! - Adds \c anObject to the end of the array. - @param anObject the object to add to the array -*/ -- (void)addObject:(id)anObject -{ - push(anObject); -} - -/*! - Adds the objects in \c anArray to the receiver array. - @param anArray the array of objects to add to the end of the receiver -*/ -- (void)addObjectsFromArray:(CPArray)anArray -{ - splice.apply(self, [length, 0].concat(anArray)); -} - -/*! - Inserts an object into the receiver at the specified location. - @param anObject the object to insert into the array - @param anIndex the location to insert \c anObject at -*/ -- (void)insertObject:(id)anObject atIndex:(int)anIndex -{ - splice(anIndex, 0, anObject); -} - -/*! - Inserts the objects in the provided array into the receiver at the indexes specified. - @param objects the objects to add to this array - @param anIndexSet the indices for the objects -*/ -- (void)insertObjects:(CPArray)objects atIndexes:(CPIndexSet)indexes -{ - var indexesCount = [indexes count], - objectsCount = [objects count]; - - if (indexesCount !== objectsCount) - [CPException raise:CPRangeException reason:"the counts of the passed-in array (" + objectsCount + ") and index set (" + indexesCount + ") must be identical."]; - - var lastIndex = [indexes lastIndex]; - - if (lastIndex >= [self count] + indexesCount) - [CPException raise:CPRangeException reason:"the last index (" + lastIndex + ") must be less than the sum of the original count (" + [self count] + ") and the insertion count (" + indexesCount + ")."]; - - var index = 0, - currentIndex = [indexes firstIndex]; - - for (; index < objectsCount; ++index, currentIndex = [indexes indexGreaterThanIndex:currentIndex]) - [self insertObject:objects[index] atIndex:currentIndex]; -} - -- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors -{ - var count = [descriptors count], - index; - - if (count) - { - index = [self _indexOfObject:anObject sortedByFunction:function(lhs, rhs) - { - var i = 0, - result = CPOrderedSame; - - while (i < count) - if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) - return result; - - return result; - } context:nil]; - - if (index < 0) - index = -index - 1; - } - else - index = self.length; - - [self insertObject:anObject atIndex:index]; - return index; -} - -/*! - Replaces the element at \c anIndex with \c anObject. - The current element at position \c anIndex will be removed from the array. - @param anIndex the position in the array to place \c anObject -*/ -- (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject -{ - self[anIndex] = anObject; -} - -/*! - Replace the elements at the indices specified by \c anIndexSet with - the objects in \c objects. - @param anIndexSet the set of indices to array positions that will be replaced - @param objects the array of objects to place in the specified indices -*/ -- (void)replaceObjectsAtIndexes:(CPIndexSet)anIndexSet withObjects:(CPArray)objects -{ - var i = 0, - index = [anIndexSet firstIndex]; - - while (index != CPNotFound) - { - [self replaceObjectAtIndex:index withObject:objects[i++]]; - index = [anIndexSet indexGreaterThanIndex:index]; - } -} - -/*! - Replaces some of the receiver's objects with objects from \c anArray. Specifically, the elements of the - receiver in the range specified by \c aRange, - with the elements of \c anArray in the range specified by \c otherRange. - @param aRange the range of elements to be replaced in the receiver - @param anArray the array to retrieve objects for placement into the receiver - @param otherRange the range of objects in \c anArray to pull from for placement into the receiver -*/ -- (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray range:(CPRange)otherRange -{ - if (!otherRange.location && otherRange.length == [anArray count]) - [self replaceObjectsInRange:aRange withObjectsFromArray:anArray]; - else - splice.apply(self, [aRange.location, aRange.length].concat([anArray subarrayWithRange:otherRange])); -} - -/*! - Replaces some of the receiver's objects with the objects from - \c anArray. Specifically, the elements of the - receiver in the range specified by \c aRange. - @param aRange the range of elements to be replaced in the receiver - @param anArray the array to retrieve objects for placement into the receiver -*/ -- (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray -{ - splice.apply(self, [aRange.location, aRange.length].concat(anArray)); -} - -/*! - Sets the contents of the receiver to be identical to the contents of \c anArray. - @param anArray the array of objects used to replace the receiver's objects -*/ -- (void)setArray:(CPArray)anArray -{ - if (self == anArray) - return; - - splice.apply(self, [0, length].concat(anArray)); -} - -// Removing Objects -/*! - Removes all objects from this array. -*/ -- (void)removeAllObjects -{ - splice(0, length); -} - -/*! - Removes the last object from the array. -*/ -- (void)removeLastObject -{ - pop(); -} - -/*! - Removes all entries of \c anObject from the array. - @param anObject the object whose entries are to be removed -*/ -- (void)removeObject:(id)anObject -{ - [self removeObject:anObject inRange:CPMakeRange(0, length)]; -} - -/*! - Removes all entries of \c anObject from the array, in the range specified by \c aRange. - @param anObject the object to remove - @param aRange the range to search in the receiver for the object -*/ -- (void)removeObject:(id)anObject inRange:(CPRange)aRange -{ - var index; - - while ((index = [self indexOfObject:anObject inRange:aRange]) != CPNotFound) - { - [self removeObjectAtIndex:index]; - aRange = CPIntersectionRange(CPMakeRange(index, length - index), aRange); - } -} - -/*! - Removes the object at \c anIndex. - @param anIndex the location of the element to be removed -*/ -- (void)removeObjectAtIndex:(int)anIndex -{ - splice(anIndex, 1); -} - -/*! - Removes the objects at the indices specified by \c CPIndexSet. - @param anIndexSet the indices of the elements to be removed from the array -*/ -- (void)removeObjectsAtIndexes:(CPIndexSet)anIndexSet -{ - var index = [anIndexSet lastIndex]; - - while (index != CPNotFound) - { - [self removeObjectAtIndex:index]; - index = [anIndexSet indexLessThanIndex:index]; - } -} - -/*! - Remove the first instance of \c anObject from the array. - The search for the object is done using \c ==. - @param anObject the object to remove -*/ -- (void)removeObjectIdenticalTo:(id)anObject -{ - [self removeObjectIdenticalTo:anObject inRange:CPMakeRange(0, [self count])]; -} - -/*! - Remove the first instance of \c anObject from the array, - within the range specified by \c aRange. - The search for the object is done using \c ==. - @param anObject the object to remove - @param aRange the range in the array to search for the object -*/ -- (void)removeObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange -{ - var index, - count = [self count]; - - while ((index = [self indexOfObjectIdenticalTo:anObject inRange:aRange]) !== CPNotFound) - { - [self removeObjectAtIndex:index]; - aRange = CPIntersectionRange(CPMakeRange(index, (--count) - index), aRange); - } -} - -/*! - Remove the objects in \c anArray from the receiver array. - @param anArray the array of objects to remove from the receiver -*/ -- (void)removeObjectsInArray:(CPArray)anArray -{ - var index = 0, - count = [anArray count]; - - for (; index < count; ++index) - [self removeObject:anArray[index]]; -} - -/*! - Removes all the objects in the specified range from the receiver. - @param aRange the range of objects to remove -*/ -- (void)removeObjectsInRange:(CPRange)aRange -{ - splice(aRange.location, aRange.length); -} - -// Rearranging objects -/*! - Swaps the elements at the two specified indices. - @param anIndex the first index to swap from - @param otherIndex the second index to swap from -*/ -- (void)exchangeObjectAtIndex:(unsigned)anIndex withObjectAtIndex:(unsigned)otherIndex -{ - var temporary = self[anIndex]; - self[anIndex] = self[otherIndex]; - self[otherIndex] = temporary; -} - -- (CPArray)sortUsingDescriptors:(CPArray)descriptors -{ - [self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors]; -} - -/*! - Sorts the receiver array using a JavaScript function as a comparator, and a specified context. - @param aFunction a JavaScript function that will be called to compare objects - @param aContext an object that will be passed to \c aFunction with comparison -*/ -- (void)sortUsingFunction:(Function)aFunction context:(id)aContext -{ - var h, - i, - j, - k, - l, - m, - n = [self count], - o; - - var A, - B = []; - - for (h = 1; h < n; h += h) - { - for (m = n - 1 - h; m >= 0; m -= h + h) - { - l = m - h + 1; - if (l < 0) - l = 0; - - for (i = 0, j = l; j <= m; i++, j++) - B[i] = self[j]; - - for (i = 0, k = l; k < j && j <= m + h; k++) - { - A = self[j]; - o = aFunction(A, B[i], aContext); - if (o >= 0) - self[k] = B[i++]; - else - { - self[k] = A; - j++; - } - } - - while (k < j) - self[k++] = B[i++]; - } - } -} - -/*! - Sorts the receiver array using an Objective-J method as a comparator. - @param aSelector the selector for the method to call for comparison -*/ -- (void)sortUsingSelector:(SEL)aSelector -{ - [self sortUsingFunction:selectorCompare context:aSelector]; -} - -@end - -var selectorCompare = function selectorCompare(object1, object2, selector) -{ - return [object1 performSelector:selector withObject:object2]; -} - -// sort using sort descriptors -var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors) -{ - var result = CPOrderedSame, - i = 0, - n = [descriptors count]; - - while (i < n && result === CPOrderedSame) - result = [descriptors[i++] compareObject:lhs withObject:rhs]; - - return result; -} - @implementation CPArray (CPCoding) - (id)initWithCoder:(CPCoder)aCoder @@ -1386,18 +850,82 @@ var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, @end -/*! - @class CPMutableArray - @ingroup compatability +/* @ignore */ +@implementation _CPArrayEnumerator : CPEnumerator +{ + CPArray _array; + int _index; +} - This class is just an empty subclass of CPArray. - CPArray already implements mutable methods and - this class only exists for source compatability. -*/ -@implementation CPMutableArray : CPArray +- (id)initWithArray:(CPArray)anArray +{ + self = [super init]; + + if (self) + { + _array = anArray; + _index = -1; + } + + return self; +} + +- (id)nextObject +{ + if (++_index >= [_array count]) + return nil; + + return [_array objectAtIndex:_index]; +} @end -Array.prototype.isa = CPArray; -[CPArray initialize]; +/* @ignore */ +@implementation _CPReverseArrayEnumerator : CPEnumerator +{ + CPArray _array; + int _index; +} +- (id)initWithArray:(CPArray)anArray +{ + self = [super init]; + + if (self) + { + _array = anArray; + _index = [_array count]; + } + + return self; +} + +- (id)nextObject +{ + if (--_index < 0) + return nil; + + return [_array objectAtIndex:_index]; +} + +@end + +var _CPSharedPlaceholderArray = nil; + +@implementation _CPPlaceholderArray : CPArray +{ +} + ++ (id)alloc +{ + if (!_CPSharedPlaceholderArray) + _CPSharedPlaceholderArray = [super alloc]; + + return _CPSharedPlaceholderArray; +} + +@end + +@import "CPArray.j" +@import "CPMutableArray.j" +@import "_CPJavaScriptArray.j" diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j new file mode 100644 index 000000000..0220e0982 --- /dev/null +++ b/Foundation/CPArray/CPMutableArray.j @@ -0,0 +1,400 @@ + +@import "CPArray.j" + + +/*! + @class CPMutableArray + @ingroup compatability + + This class is just an empty subclass of CPArray. + CPArray already implements mutable methods and + this class only exists for source compatability. +*/ +@implementation CPMutableArray : CPArray +{ +} + +// Creating arrays +/*! + Creates an array able to store at least \c aCapacity + items. Because CPArray is backed by JavaScript arrays, + this method ends up simply returning a regular array. +*/ ++ (CPArray)arrayWithCapacity:(unsigned)aCapacity +{ + return [[self alloc] initWithCapacity:aCapacity]; +} + +/*! + Initializes an array able to store at least \c aCapacity items. Because CPArray + is backed by JavaScript arrays, this method ends up simply returning a regular array. +*/ +- (id)initWithCapacity:(unsigned)aCapacity +{ + return self; +} + +// Adding and replacing objects +/*! + Adds \c anObject to the end of the array. + @param anObject the object to add to the array +*/ +- (void)addObject:(id)anObject +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Adds the objects in \c anArray to the receiver array. + @param anArray the array of objects to add to the end of the receiver +*/ +- (void)addObjectsFromArray:(CPArray)anArray +{ + splice.apply(self, [length, 0].concat(anArray)); +} + +/*! + Inserts an object into the receiver at the specified location. + @param anObject the object to insert into the array + @param anIndex the location to insert \c anObject at +*/ +- (void)insertObject:(id)anObject atIndex:(int)anIndex +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Inserts the objects in the provided array into the receiver at the indexes specified. + @param objects the objects to add to this array + @param anIndexSet the indices for the objects +*/ +- (void)insertObjects:(CPArray)objects atIndexes:(CPIndexSet)indexes +{ + var indexesCount = [indexes count], + objectsCount = [objects count]; + + if (indexesCount !== objectsCount) + [CPException raise:CPRangeException reason:"the counts of the passed-in array (" + objectsCount + ") and index set (" + indexesCount + ") must be identical."]; + + var lastIndex = [indexes lastIndex]; + + if (lastIndex >= [self count] + indexesCount) + [CPException raise:CPRangeException reason:"the last index (" + lastIndex + ") must be less than the sum of the original count (" + [self count] + ") and the insertion count (" + indexesCount + ")."]; + + var index = 0, + currentIndex = [indexes firstIndex]; + + for (; index < objectsCount; ++index, currentIndex = [indexes indexGreaterThanIndex:currentIndex]) + [self insertObject:objects[index] atIndex:currentIndex]; +} + +- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors +{ + var count = [descriptors count], + index; + + if (count) + { + // FIXME: UGH + index = [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) + { + var i = 0, + result = CPOrderedSame; + + while (i < count) + if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) + return result; + + return result; + } context:nil]; + + if (index < 0) + index = -index - 1; + } + else + index = self.length; + + [self insertObject:anObject atIndex:index]; + return index; +} + +/*! + Replaces the element at \c anIndex with \c anObject. + The current element at position \c anIndex will be removed from the array. + @param anIndex the position in the array to place \c anObject +*/ +- (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Replace the elements at the indices specified by \c anIndexSet with + the objects in \c objects. + @param anIndexSet the set of indices to array positions that will be replaced + @param objects the array of objects to place in the specified indices +*/ +- (void)replaceObjectsAtIndexes:(CPIndexSet)anIndexSet withObjects:(CPArray)objects +{ + var i = 0, + index = [anIndexSet firstIndex]; + + while (index != CPNotFound) + { + [self replaceObjectAtIndex:index withObject:objects[i++]]; + index = [anIndexSet indexGreaterThanIndex:index]; + } +} + +/*! + Replaces some of the receiver's objects with objects from \c anArray. Specifically, the elements of the + receiver in the range specified by \c aRange, + with the elements of \c anArray in the range specified by \c otherRange. + @param aRange the range of elements to be replaced in the receiver + @param anArray the array to retrieve objects for placement into the receiver + @param otherRange the range of objects in \c anArray to pull from for placement into the receiver +*/ +- (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray range:(CPRange)otherRange +{ + if (!otherRange.location && otherRange.length == [anArray count]) + [self replaceObjectsInRange:aRange withObjectsFromArray:anArray]; + else + splice.apply(self, [aRange.location, aRange.length].concat([anArray subarrayWithRange:otherRange])); +} + +/*! + Replaces some of the receiver's objects with the objects from + \c anArray. Specifically, the elements of the + receiver in the range specified by \c aRange. + @param aRange the range of elements to be replaced in the receiver + @param anArray the array to retrieve objects for placement into the receiver +*/ +- (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray +{ + splice.apply(self, [aRange.location, aRange.length].concat(anArray)); +} + +/*! + Sets the contents of the receiver to be identical to the contents of \c anArray. + @param anArray the array of objects used to replace the receiver's objects +*/ +- (void)setArray:(CPArray)anArray +{ + if (self == anArray) + return; + + splice.apply(self, [0, length].concat(anArray)); +} + +// Removing Objects +/*! + Removes all objects from this array. +*/ +- (void)removeAllObjects +{ + splice(0, length); +} + +/*! + Removes the last object from the array. +*/ +- (void)removeLastObject +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Removes all entries of \c anObject from the array. + @param anObject the object whose entries are to be removed +*/ +- (void)removeObject:(id)anObject +{ + [self removeObject:anObject inRange:CPMakeRange(0, length)]; +} + +/*! + Removes all entries of \c anObject from the array, in the range specified by \c aRange. + @param anObject the object to remove + @param aRange the range to search in the receiver for the object +*/ +- (void)removeObject:(id)anObject inRange:(CPRange)aRange +{ + var index; + + while ((index = [self indexOfObject:anObject inRange:aRange]) != CPNotFound) + { + [self removeObjectAtIndex:index]; + aRange = CPIntersectionRange(CPMakeRange(index, length - index), aRange); + } +} + +/*! + Removes the object at \c anIndex. + @param anIndex the location of the element to be removed +*/ +- (void)removeObjectAtIndex:(int)anIndex +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Removes the objects at the indices specified by \c CPIndexSet. + @param anIndexSet the indices of the elements to be removed from the array +*/ +- (void)removeObjectsAtIndexes:(CPIndexSet)anIndexSet +{ + var index = [anIndexSet lastIndex]; + + while (index != CPNotFound) + { + [self removeObjectAtIndex:index]; + index = [anIndexSet indexLessThanIndex:index]; + } +} + +/*! + Remove the first instance of \c anObject from the array. + The search for the object is done using \c ==. + @param anObject the object to remove +*/ +- (void)removeObjectIdenticalTo:(id)anObject +{ + [self removeObjectIdenticalTo:anObject inRange:CPMakeRange(0, [self count])]; +} + +/*! + Remove the first instance of \c anObject from the array, + within the range specified by \c aRange. + The search for the object is done using \c ==. + @param anObject the object to remove + @param aRange the range in the array to search for the object +*/ +- (void)removeObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange +{ + var index, + count = [self count]; + + while ((index = [self indexOfObjectIdenticalTo:anObject inRange:aRange]) !== CPNotFound) + { + [self removeObjectAtIndex:index]; + aRange = CPIntersectionRange(CPMakeRange(index, (--count) - index), aRange); + } +} + +/*! + Remove the objects in \c anArray from the receiver array. + @param anArray the array of objects to remove from the receiver +*/ +- (void)removeObjectsInArray:(CPArray)anArray +{ + var index = 0, + count = [anArray count]; + + for (; index < count; ++index) + [self removeObject:anArray[index]]; +} + +/*! + Removes all the objects in the specified range from the receiver. + @param aRange the range of objects to remove +*/ +- (void)removeObjectsInRange:(CPRange)aRange +{ + splice(aRange.location, aRange.length); +} + +// Rearranging objects +/*! + Swaps the elements at the two specified indices. + @param anIndex the first index to swap from + @param otherIndex the second index to swap from +*/ +- (void)exchangeObjectAtIndex:(unsigned)anIndex withObjectAtIndex:(unsigned)otherIndex +{ + var temporary = self[anIndex]; + self[anIndex] = self[otherIndex]; + self[otherIndex] = temporary; +} + +- (CPArray)sortUsingDescriptors:(CPArray)descriptors +{ + [self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors]; +} + +/*! + Sorts the receiver array using a JavaScript function as a comparator, and a specified context. + @param aFunction a JavaScript function that will be called to compare objects + @param aContext an object that will be passed to \c aFunction with comparison +*/ +- (void)sortUsingFunction:(Function)aFunction context:(id)aContext +{ + var h, + i, + j, + k, + l, + m, + n = [self count], + o; + + var A, + B = []; + + for (h = 1; h < n; h += h) + { + for (m = n - 1 - h; m >= 0; m -= h + h) + { + l = m - h + 1; + if (l < 0) + l = 0; + + for (i = 0, j = l; j <= m; i++, j++) + B[i] = self[j]; + + for (i = 0, k = l; k < j && j <= m + h; k++) + { + A = self[j]; + o = aFunction(A, B[i], aContext); + if (o >= 0) + self[k] = B[i++]; + else + { + self[k] = A; + j++; + } + } + + while (k < j) + self[k++] = B[i++]; + } + } +} + +/*! + Sorts the receiver array using an Objective-J method as a comparator. + @param aSelector the selector for the method to call for comparison +*/ +- (void)sortUsingSelector:(SEL)aSelector +{ + [self sortUsingFunction:selectorCompare context:aSelector]; +} + +@end + +var selectorCompare = function selectorCompare(object1, object2, selector) +{ + return [object1 performSelector:selector withObject:object2]; +} + +// sort using sort descriptors +var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors) +{ + var result = CPOrderedSame, + i = 0, + n = [descriptors count]; + + while (i < n && result === CPOrderedSame) + result = [descriptors[i++] compareObject:lhs withObject:rhs]; + + return result; +} diff --git a/Foundation/CPArray/_CPJavaScriptArray.j b/Foundation/CPArray/_CPJavaScriptArray.j new file mode 100644 index 000000000..06e332145 --- /dev/null +++ b/Foundation/CPArray/_CPJavaScriptArray.j @@ -0,0 +1,226 @@ + +@import "CPMutableArray.j" + + +@implementation _CPJavaScriptArray : CPMutableArray +{ +} + ++ (id)alloc +{ + return []; +} + ++ (CPArray)array +{ + return []; +} + ++ (id)arrayWithArray:(CPArray)anArray +{ + if (anArray.isa === _CPJavaScriptArray) + return anArray.slice(0); + + return [[self alloc] initWithArray:anArray]; +} + ++ (id)arrayWithObject:(id)anObject +{ + return [anObject]; +} + +- (id)initWithArray:(CPArray)anArray +{ + return anArray.slice(0); +} + +- (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems +{ + if (!shouldCopyItems) + return anArray.slice(0); + + self = [super init]; + + var index = 0; + + if (anArray.isa === _CPJavaScriptArray) + { + var count = anArray.length; + + for (; index < count; ++index) + { + var object = anArray[index]; + + self[index] = object.isa ? [object copy] : object; + } + + return self; + } + + var count = [anArray count]; + + for (; index < count; ++index) + { + var object = [anArray objectatIndex:index]; + + self[index] = object.isa ? [object copy] : object; + } + + return self; +} + +- (id)initWithObjects:(id)anObject, ... +{ + // The arguments array contains self and _cmd, so the first object is at position 2. + var index = 2, + count = arguments.length; + + for (; index < count; ++index) + if (arguments[index] === nil) + break; + + return Array.prototype.slice.call(arguments, 2, index); +} + +- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount +{ + if (objects.isa === _CPJavaScriptArray) + return objects.slice(0); + + var array = [], + index = 0; + + for (; index < aCount; ++index) + array.push([objects objectAtIndex:index]); + + return array; +} + +- (BOOL)count +{ + return self.length; +} + +- (id)objectAtIndex:(CPUInteger)anIndex +{ + if (anIndex >= self.length || anIndex < 0) + [CPException raise:CPRangeException + reason:@"index (" + anIndex + @") beyond bounds (" + self.length + @")"]; + + return self[anIndex]; +} + +- (CPUInteger)indexOfObject:(id)anObject +{ + return [self indexOfObject:anObject inRange:nil]; +} + +- (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange +{ + // Only use isEqual: if our object is a CPObject. + if (anObject && anObject.isa) + { + var index = aRange ? aRange.location : 0, + count = aRange ? CPMaxRange(aRange) : self.length; + + for (; index < count; ++index) + if ([self[index] isEqual:anObject]) + return index; + + return CPNotFound; + } + + return [self indexOfObjectIdenticalTo:anObject inRange:aRange]; +} + +- (CPUInteger)indexOfObjectIdenticalTo:(id)anObject +{ + return [self indexOfObjectIdenticalTo:anObject inRange:nil]; +} + +- (CPUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange +{ + if (self.indexOf) + return self.indexOf(anObject); + + var index = aRange ? aRange.location : 0, + count = aRange ? CPMaxRange(aRange) : self.length; + + for (; index < count; ++index) + if (self[index] === anObject) + return index; + + return CPNotFound; +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects +{ + if (!aSelector) + [CPException raise:CPInvalidArgumentException + reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"]; + + var index = 0, + count = self.length; + + if ([objects count]) + { + argumentsArray = [[nil, aSelector] arrayByAddingObjectsFromArray:objects]; + + for (; index < count; ++index) + { + argumentsArray[0] = self[index]; + objj_msgSend.apply(this, argumentsArray); + } + } + + else + for (; index < count; ++index) + objj_msgSend(self[index], aSelector); +} + +- (CPArray)subarrayWithRange:(CPRange)aRange +{ + if (aRange.location < 0 || CPMaxRange(aRange) > self.length) + [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"]; + + return self.slice(aRange.location, CPMaxRange(aRange)); +} + +- (CPString)componentsJoinedByString:(CPString)aString +{ + return self.join(aString); +} + +- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex +{ + self.splice(anIndex, 0, anObject); +} + +- (void)removeObjectAtIndex:(CPUInteger)anIndex +{ + self.splice(anIndex, 1); +} + +- (void)addObject:(id)anObject +{ + self.push(anObject); +} + +- (void)removeLastObject +{ + self.pop(); +} + +- (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject +{ + self[anIndex] = anObject; +} + +- (Class)classForCoder +{ + return CPArray; +} + +@end + +Array.prototype.isa = _CPJavaScriptArray; diff --git a/Foundation/CPKeyedArchiver.j b/Foundation/CPKeyedArchiver.j index d933458a7..904ed8f18 100644 --- a/Foundation/CPKeyedArchiver.j +++ b/Foundation/CPKeyedArchiver.j @@ -385,7 +385,7 @@ var _CPKeyedArchiverStringClass = Nil, { var i = 0, count = objects.length, - references = [CPArray arrayWithCapacity:count]; + references = []; for (; i < count; ++i) [references addObject:_CPKeyedArchiverEncodeObject(self, objects[i], NO)]; diff --git a/Foundation/CPKeyedUnarchiver.j b/Foundation/CPKeyedUnarchiver.j index a98853038..cdd501ef3 100644 --- a/Foundation/CPKeyedUnarchiver.j +++ b/Foundation/CPKeyedUnarchiver.j @@ -335,7 +335,7 @@ var CPArrayClass = Ni else if (objectClass === CPNumberClass || objectClass === CPDataClass || objectClass === CPStringClass) return object; - else if (objectClass === CPArrayClass || objectClass === CPMutableArrayClass) + else if (objectClass === _CPJavaScriptArray) { var index = 0, count = object.length, diff --git a/Foundation/CPPredicate/CPPredicate.j b/Foundation/CPPredicate/CPPredicate.j index 7f323b047..d9df99faf 100644 --- a/Foundation/CPPredicate/CPPredicate.j +++ b/Foundation/CPPredicate/CPPredicate.j @@ -536,12 +536,13 @@ function(newValue)\ if ([self scanString:@"{" intoString:NULL]) { - var a = [CPMutableArray arrayWithCapacity:10]; - if ([self scanString:@"}" intoString:NULL]) return [CPExpression expressionForConstantValue:a]; + var a = []; + [a addObject:[self parseExpression]]; + while ([self scanString:@"," intoString:NULL]) [a addObject:[self parseExpression]]; @@ -779,7 +780,7 @@ function(newValue)\ { // function - this parser allows for (max)(a, b, c) to be properly // recognized and even (%K)(a, b, c) if %K evaluates to "max" - var args = [CPMutableArray arrayWithCapacity:5]; + var args = []; if (![left keyPath]) CPRaiseParseError(self, @"expression"); diff --git a/Foundation/CPSet.j b/Foundation/CPSet.j index d6aef74ae..c68bdc641 100644 --- a/Foundation/CPSet.j +++ b/Foundation/CPSet.j @@ -85,12 +85,12 @@ */ + (id)setWithObjects:(id)anObject, ... { - var args = Array.prototype.slice.apply(arguments); + var argumentsArray = Array.prototype.slice.apply(arguments); - args[0] = [self alloc]; - args[1] = @selector(initWithObjects:); + argumentsArray[0] = [self alloc]; + argumentsArray[1] = @selector(initWithObjects:); - return objj_msgSend.apply(this, args); + return objj_msgSend.apply(this, argumentsArray); } /* diff --git a/Tests/Foundation/CPArrayTest.j b/Tests/Foundation/CPArrayTest.j index 14b528108..0f4fa99aa 100644 --- a/Tests/Foundation/CPArrayTest.j +++ b/Tests/Foundation/CPArrayTest.j @@ -83,11 +83,11 @@ - (void)testRemoveObjectsAtIndexes { var array = [CPMutableArray arrayWithObjects:@"one", @"two", @"three", @"four", nil], - indexes = [CPMutableIndexSet indexSetWithIndex: 2]; + indexes = [CPMutableIndexSet indexSetWithIndex:2]; [array removeObjectsAtIndexes:indexes]; - [self assert:array equals:[@"one", @"two", @"four", nil]]; + [self assert:array equals:[@"one", @"two", @"four"]]; } - (void)testIndexOfObjectPassingTest