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