mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
1421 lines
40 KiB
Plaintext
Executable File
1421 lines
40 KiB
Plaintext
Executable File
/*
|
|
* CPArray.j
|
|
* Foundation
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
@import "CPEnumerator.j"
|
|
@import "CPException.j"
|
|
@import "CPObject.j"
|
|
@import "CPRange.j"
|
|
@import "CPSortDescriptor.j"
|
|
|
|
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
|
|
|
|
/*!
|
|
@class CPArray
|
|
@brief A mutable array backed by a JavaScript Array.
|
|
@ingroup foundation
|
|
|
|
A mutable array class backed by a JavaScript Array.
|
|
There is also a CPMutableArray class,
|
|
but it is just a child class of this class with an
|
|
empty implementation. All mutable functionality is
|
|
implemented directly in CPArray.
|
|
*/
|
|
@implementation CPArray : CPObject
|
|
|
|
/*!
|
|
Returns a new uninitialized CPArray.
|
|
*/
|
|
+ (id)alloc
|
|
{
|
|
if ([self class] !== CPArray && [self class] !== CPMutableArray) return [super alloc];
|
|
return [];
|
|
}
|
|
|
|
/*!
|
|
Returns a new initialized CPArray.
|
|
*/
|
|
+ (id)array
|
|
{
|
|
return [[self alloc] init];
|
|
}
|
|
|
|
/*!
|
|
Creates a new array containing the objects in \c anArray.
|
|
@param anArray Objects in this array will be added to the new array
|
|
@return a new CPArray of the provided objects
|
|
*/
|
|
+ (id)arrayWithArray:(CPArray)anArray
|
|
{
|
|
return [[self alloc] initWithArray:anArray];
|
|
}
|
|
|
|
/*!
|
|
Creates a new array with \c anObject in it.
|
|
@param anObject the object to be added to the array
|
|
@return a new CPArray containing a single object
|
|
*/
|
|
+ (id)arrayWithObject:(id)anObject
|
|
{
|
|
return [[self alloc] initWithObjects:anObject];
|
|
}
|
|
|
|
/*!
|
|
Creates a new CPArray containing all the objects passed as arguments to the method.
|
|
@param anObject the objects that will be added to the new array
|
|
@return a new CPArray containing the argument objects
|
|
*/
|
|
+ (id)arrayWithObjects:(id)anObject, ...
|
|
{
|
|
var i = 2,
|
|
array = [[self alloc] init],
|
|
count = arguments.length;
|
|
|
|
for (; i < count; ++i)
|
|
array.push(arguments[i]);
|
|
|
|
return array;
|
|
}
|
|
|
|
/*!
|
|
Creates a CPArray from a JavaScript array of objects.
|
|
@param objects the JavaScript Array
|
|
@param aCount the number of objects in the JS Array
|
|
@return a new CPArray containing the specified objects
|
|
*/
|
|
+ (id)arrayWithObjects:(id)objects count:(unsigned)aCount
|
|
{
|
|
return [[self alloc] initWithObjects:objects count:aCount];
|
|
}
|
|
|
|
/*!
|
|
Initializes the CPArray.
|
|
@return the initialized array
|
|
*/
|
|
- (id)init
|
|
{
|
|
return self;
|
|
}
|
|
|
|
// Creating an Array
|
|
/*!
|
|
Creates a new CPArray from \c anArray.
|
|
@param anArray objects in this array will be added to the new array
|
|
@return a new CPArray containing the objects of \c anArray
|
|
*/
|
|
- (id)initWithArray:(CPArray)anArray
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
[self setArray:anArray];
|
|
|
|
return self;
|
|
}
|
|
|
|
/*!
|
|
Initializes a the array with the contents of \c anArray
|
|
and optionally performs a deep copy of the objects based on \c copyItems.
|
|
@param anArray the array to copy the data from
|
|
@param copyItems if \c YES, each object will be copied by having a \c -copy message sent to it, and the
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*!
|
|
initializes an array with the contents of anArray
|
|
*/
|
|
- (id)initWithObjects:(Array)anArray, ...
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
/*!
|
|
Initializes the array with a JavaScript array of objects.
|
|
@param objects the array of objects to add to the receiver
|
|
@param aCount the number of objects in \c objects
|
|
@return the initialized CPArray
|
|
*/
|
|
- (id)initWithObjects:(id)objects count:(unsigned)aCount
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
var index = 0;
|
|
|
|
for (; index < aCount; ++index)
|
|
push(objects[index]);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
// Querying an array
|
|
/*!
|
|
Returns \c YES if the array contains \c anObject. Otherwise, it returns \c NO.
|
|
@param anObject the method checks if this object is already in the array
|
|
*/
|
|
- (BOOL)containsObject:(id)anObject
|
|
{
|
|
return [self indexOfObject:anObject] != CPNotFound;
|
|
}
|
|
|
|
/*!
|
|
Returns the number of elements in the array
|
|
*/
|
|
- (int)count
|
|
{
|
|
return length;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in this array.
|
|
If the object is not in the array,
|
|
returns \c CPNotFound. It first attempts to find
|
|
a match using \c -isEqual:, then \c ===.
|
|
@param anObject the object to search for
|
|
*/
|
|
- (int)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;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array
|
|
within \c aRange. It first attempts to find
|
|
a match using \c -isEqual:, then \c ===.
|
|
@param anObject the object to search for
|
|
@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
|
|
{
|
|
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;
|
|
|
|
return CPNotFound;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array. The test for equality is done using only \c ===.
|
|
@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
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array
|
|
within \c aRange. The test for equality is
|
|
done using only \c ==.
|
|
@param anObject the object to search for
|
|
@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
|
|
{
|
|
// If indexOf exists, use it since it's probably
|
|
// faster than anything we can implement.
|
|
if (self.indexOf)
|
|
{
|
|
var index = indexOf(anObject, aRange.location);
|
|
|
|
if (CPLocationInRange(index, aRange))
|
|
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;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of the first object in the receiver that passes a test in a given Javascript function.
|
|
@param predicate The function to apply to elements of the array. The function receives two arguments:
|
|
object The element in the array.
|
|
index The index of the element in the array.
|
|
The predicate function should either return a Boolean value that indicates whether the object passed the test,
|
|
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
|
|
{
|
|
return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate context:undefined];
|
|
}
|
|
|
|
/*!
|
|
Returns the index of the first object in the receiver that passes a test in a given Javascript function.
|
|
@param predicate The function to apply to elements of the array. The function receives two arguments:
|
|
object The element in the array.
|
|
index The index of the element in the array.
|
|
context The object passed to the receiver in the aContext parameter.
|
|
The predicate function should either return a Boolean value that indicates whether the object passed the test,
|
|
or nil to stop the search, which will return CPNotFound to the sender.
|
|
@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
|
|
{
|
|
return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate 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
|
|
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.
|
|
index The index of the element in the array.
|
|
The predicate function should either return a Boolean value that indicates whether the object passed the test,
|
|
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
|
|
{
|
|
return [self indexOfObjectWithOptions:opts passingTest:predicate 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
|
|
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.
|
|
index The index of the element in the array.
|
|
context The object passed to the receiver in the aContext parameter.
|
|
The predicate function should either return a Boolean value that indicates whether the object passed the test,
|
|
or nil to stop the search, which will return CPNotFound to the sender.
|
|
@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
|
|
{
|
|
// 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)
|
|
{
|
|
start = [self count] - 1;
|
|
stop = -1;
|
|
increment = -1;
|
|
}
|
|
else
|
|
{
|
|
start = 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;
|
|
}
|
|
|
|
return CPNotFound;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array, which must be sorted in the same order as
|
|
calling sortUsingSelector: with the selector passed to this method would result in.
|
|
@param anObject the object to search for
|
|
@param aSelector the comparison selector to call on each item in the list, the same
|
|
selector should have been used to sort the array (or to maintain its sorted order).
|
|
@return the index of the object, or \c CPNotFound if it was not found.
|
|
*/
|
|
- (unsigned)indexOfObject:(id)anObject sortedBySelector:(SEL)aSelector
|
|
{
|
|
return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) { objj_msgSend(lhs, aSelector, rhs); }];
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array, which must be sorted in the same order as
|
|
calling sortUsingFunction: with the selector passed to this method would result in.
|
|
The function will be called like so:
|
|
<pre>
|
|
aFunction(anObject, currentObjectInArrayForComparison)
|
|
</pre>
|
|
@param anObject the object to search for
|
|
@param aFunction the comparison function to call on each item in the array that we search. the same
|
|
selector should have been used to sort the array (or to maintain its sorted order).
|
|
@return the index of the object, or \c CPNotFound if it was not found.
|
|
*/
|
|
- (unsigned)indexOfObject:(id)anObject sortedByFunction:(Function)aFunction
|
|
{
|
|
return [self indexOfObject:anObject sortedByFunction:aFunction context:nil];
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array, which must be sorted in the same order as
|
|
calling sortUsingFunction: with the selector passed to this method would result in.
|
|
The function will be called like so:
|
|
<pre>
|
|
aFunction(anObject, currentObjectInArrayForComparison, context)
|
|
</pre>
|
|
@param anObject the object to search for
|
|
@param aFunction the comparison function to call on each item in the array that we search. the same
|
|
function should have been used to sort the array (or to maintain its sorted order).
|
|
@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
|
|
{
|
|
if (!aFunction)
|
|
return CPNotFound;
|
|
|
|
if (length === 0)
|
|
return -1;
|
|
|
|
var mid,
|
|
c,
|
|
first = 0,
|
|
last = length - 1;
|
|
|
|
while (first <= last)
|
|
{
|
|
mid = FLOOR((first + last) / 2);
|
|
c = aFunction(anObject, self[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)
|
|
mid++;
|
|
|
|
return mid;
|
|
}
|
|
}
|
|
|
|
return -first - 1;
|
|
}
|
|
|
|
/*!
|
|
Returns the index of \c anObject in the array, which must be sorted in the same order as
|
|
calling sortUsingDescriptors: with the descriptors passed to this method would result in.
|
|
@param anObject the object to search for
|
|
@param descriptors the array of descriptors to use to compare each item in the array that we search. the same
|
|
descriptors should have been used to sort the array (or to maintain its sorted order).
|
|
@return the index of the object, or \c CPNotFound if it was not found.
|
|
*/
|
|
- (unsigned)indexOfObject:(id)anObject sortedByDescriptors:(CPArray)descriptors
|
|
{
|
|
var count = [descriptors count];
|
|
|
|
return [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;
|
|
}];
|
|
}
|
|
|
|
- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors
|
|
{
|
|
if (!descriptors || ![descriptors count])
|
|
{
|
|
[self addObject:anObject];
|
|
return [self count] - 1;
|
|
}
|
|
|
|
var index = [self _insertObject:anObject sortedByFunction:function(lhs, rhs)
|
|
{
|
|
var i = 0,
|
|
count = [descriptors count],
|
|
result = CPOrderedSame;
|
|
|
|
while (i < count)
|
|
if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
|
|
return result;
|
|
|
|
return result;
|
|
} context:nil];
|
|
|
|
if (index < 0)
|
|
index = -result-1;
|
|
|
|
[self insertObject:anObject atIndex:index];
|
|
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.
|
|
@param aSelector the selector of the message to send
|
|
@throws CPInvalidArgumentException if \c aSelector is \c nil
|
|
*/
|
|
- (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);
|
|
}
|
|
|
|
/*!
|
|
Sends each element in the array a message with an argument.
|
|
@param aSelector the selector of the message to send
|
|
@param anObject the first argument of the message
|
|
@throws CPInvalidArgumentException if \c aSelector is \c nil
|
|
*/
|
|
- (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);
|
|
}
|
|
|
|
- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
|
|
{
|
|
if (!aSelector)
|
|
[CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"];
|
|
|
|
var index = 0,
|
|
count = length,
|
|
argumentsArray = [nil, aSelector].concat(objects || []);
|
|
|
|
for (; index < count; ++index)
|
|
{
|
|
argumentsArray[0] = self[index];
|
|
objj_msgSend.apply(this, argumentsArray);
|
|
}
|
|
}
|
|
|
|
|
|
// Comparing arrays
|
|
/*!
|
|
Returns the first object found in the receiver (starting at index 0) which is present in the
|
|
\c otherArray as determined by using the \c -containsObject: method.
|
|
@return the first object found, or \c nil if no common object was found.
|
|
*/
|
|
- (id)firstObjectCommonWithArray:(CPArray)anArray
|
|
{
|
|
if (![anArray count] || ![self count])
|
|
return nil;
|
|
|
|
var i = 0,
|
|
count = [self count];
|
|
|
|
for (; i < count; ++i)
|
|
if ([anArray containsObject:self[i]])
|
|
return self[i];
|
|
|
|
return nil;
|
|
}
|
|
|
|
/*!
|
|
Returns true if anArray contains exactly the same objects as the reciever.
|
|
*/
|
|
- (BOOL)isEqualToArray:(id)anArray
|
|
{
|
|
if (self === anArray)
|
|
return YES;
|
|
|
|
if (anArray === nil || length !== anArray.length)
|
|
return NO;
|
|
|
|
var index = 0,
|
|
count = [self count];
|
|
|
|
for (; index < count; ++index)
|
|
{
|
|
var lhs = self[index],
|
|
rhs = anArray[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]))
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)anObject
|
|
{
|
|
if (self === anObject)
|
|
return YES;
|
|
|
|
if (![anObject isKindOfClass:[CPArray class]])
|
|
return NO;
|
|
|
|
return [self isEqualToArray:anObject];
|
|
}
|
|
|
|
// Deriving new arrays
|
|
/*!
|
|
Returns a copy of this array plus \c anObject inside the copy.
|
|
@param anObject the object to be added to the array copy
|
|
@throws CPInvalidArgumentException if \c anObject is \c nil
|
|
@return a new array that should be n+1 in size compared to the receiver.
|
|
*/
|
|
- (CPArray)arrayByAddingObject:(id)anObject
|
|
{
|
|
var array = [self copy];
|
|
array.push(anObject);
|
|
|
|
return array;
|
|
}
|
|
|
|
/*!
|
|
Returns a new array which is the concatenation of \c self and otherArray (in this precise order).
|
|
@param anArray the array that will be concatenated to the receiver's copy
|
|
*/
|
|
- (CPArray)arrayByAddingObjectsFromArray:(CPArray)anArray
|
|
{
|
|
return slice(0).concat(anArray);
|
|
}
|
|
|
|
/*
|
|
- (CPArray)filteredArrayUsingPredicate:(CPPredicate)aPredicate
|
|
{
|
|
var i= 0,
|
|
count = [self count],
|
|
array = [CPArray array];
|
|
|
|
for (; i<count; ++i)
|
|
if (aPredicate.evaluateWithObject(self[i]))
|
|
array.push(self[i]);
|
|
|
|
return array;
|
|
}
|
|
*/
|
|
|
|
/*!
|
|
Returns a subarray of the receiver containing the objects found in the specified range \c aRange.
|
|
@param aRange the range of objects to be copied into the subarray
|
|
@throws CPRangeException if the specified range exceeds the bounds of the array
|
|
*/
|
|
- (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));
|
|
}
|
|
|
|
// Sorting arrays
|
|
/*
|
|
Not yet described.
|
|
*/
|
|
- (CPArray)sortedArrayUsingDescriptors:(CPArray)descriptors
|
|
{
|
|
var sorted = [self copy];
|
|
|
|
[sorted sortUsingDescriptors:descriptors];
|
|
|
|
return sorted;
|
|
}
|
|
|
|
/*!
|
|
Return a copy of the receiver sorted using the function passed into the first parameter.
|
|
*/
|
|
- (CPArray)sortedArrayUsingFunction:(Function)aFunction
|
|
{
|
|
return [self sortedArrayUsingFunction:aFunction context:nil];
|
|
}
|
|
|
|
/*!
|
|
Returns an array in which the objects are ordered according
|
|
to a sort with \c aFunction. This invokes
|
|
\c -sortUsingFunction:context.
|
|
@param aFunction a JavaScript 'Function' type that compares objects
|
|
@param aContext context information
|
|
@return a new sorted array
|
|
*/
|
|
- (CPArray)sortedArrayUsingFunction:(Function)aFunction context:(id)aContext
|
|
{
|
|
var sorted = [self copy];
|
|
|
|
[sorted sortUsingFunction:aFunction context:aContext];
|
|
|
|
return sorted;
|
|
}
|
|
|
|
/*!
|
|
Returns a new array in which the objects are ordered according to a sort with \c aSelector.
|
|
@param aSelector the selector that will perform object comparisons
|
|
*/
|
|
- (CPArray)sortedArrayUsingSelector:(SEL)aSelector
|
|
{
|
|
var sorted = [self copy];
|
|
|
|
[sorted sortUsingSelector:aSelector];
|
|
|
|
return sorted;
|
|
}
|
|
|
|
// Working with string elements
|
|
|
|
/*!
|
|
Returns a string formed by concatenating the objects in the
|
|
receiver, with the specified separator string inserted between each part.
|
|
If the element is a Objective-J object, then the \c -description
|
|
of that object will be used, otherwise the default JavaScript representation will be used.
|
|
@param aString the separator that will separate each object string
|
|
@return the string representation of the array
|
|
*/
|
|
- (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);
|
|
}
|
|
|
|
// Creating a description of the array
|
|
|
|
/*!
|
|
Returns a human readable description of this array and it's elements.
|
|
*/
|
|
- (CPString)description
|
|
{
|
|
var index = 0,
|
|
count = [self count],
|
|
description = '(';
|
|
|
|
for (; index < count; ++index)
|
|
{
|
|
if (index === 0)
|
|
description += '\n';
|
|
|
|
var object = [self objectAtIndex:index],
|
|
objectDescription = object && object.isa ? [object description] : String(object);
|
|
|
|
description += "\t" + objectDescription.split('\n').join("\n\t");
|
|
|
|
if (index !== count - 1)
|
|
description += ", ";
|
|
|
|
description += '\n';
|
|
}
|
|
|
|
return description + ')';
|
|
}
|
|
|
|
// Collecting paths
|
|
/*!
|
|
Returns a new array subset formed by selecting the elements that have
|
|
filename extensions from \c filterTypes. Only elements
|
|
that are of type CPString are candidates for inclusion in the returned array.
|
|
@param filterTypes an array of CPString objects that contain file extensions (without the '.')
|
|
@return a new array with matching paths
|
|
*/
|
|
- (CPArray)pathsMatchingExtensions:(CPArray)filterTypes
|
|
{
|
|
var index = 0,
|
|
count = [self count],
|
|
array = [];
|
|
|
|
for (; index < count; ++index)
|
|
if (self[index].isa && [self[index] isKindOfClass:[CPString class]] && [filterTypes containsObject:[self[index] pathExtension]])
|
|
array.push(self[index]);
|
|
|
|
return array;
|
|
}
|
|
|
|
// Key value coding
|
|
/*!
|
|
Sets the key-value for each element in the array.
|
|
@param aValue the value for the coding
|
|
@param aKey the key for the coding
|
|
*/
|
|
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
|
{
|
|
var i = 0,
|
|
count = [self count];
|
|
|
|
for (; i < count; ++i)
|
|
[self[i] setValue:aValue forKey:aKey];
|
|
}
|
|
|
|
/*!
|
|
Returns the value for \c aKey from each element in the array.
|
|
@param aKey the key to return the value for
|
|
@return an array of containing a value for each element in the array
|
|
*/
|
|
- (CPArray)valueForKey:(CPString)aKey
|
|
{
|
|
var i = 0,
|
|
count = [self count],
|
|
array = [];
|
|
|
|
for (; i < count; ++i)
|
|
array.push([self[i] valueForKey:aKey]);
|
|
|
|
return array;
|
|
}
|
|
|
|
// Copying arrays
|
|
|
|
/*!
|
|
Makes a copy of the receiver.
|
|
@return a new CPArray copy
|
|
*/
|
|
- (id)copy
|
|
{
|
|
return slice(0);
|
|
}
|
|
|
|
@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];
|
|
|
|
var 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;
|
|
|
|
[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 == CPOrderedDescending || o == CPOrderedSame)
|
|
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
|
|
{
|
|
return [aCoder decodeObjectForKey:@"CP.objects"];
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
[aCoder _encodeArrayOfObjects:self forKey:@"CP.objects"];
|
|
}
|
|
|
|
@end
|
|
|
|
/*!
|
|
@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
|
|
|
|
@end
|
|
|
|
Array.prototype.isa = CPArray;
|
|
[CPArray initialize];
|
|
|