From e45ed19dc730b9ddc2c6037ba7bcb752f580f2e9 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 25 Dec 2010 21:10:32 -0800 Subject: [PATCH] Fix remaining failing tests and add indexOfObject:inSortedRange:options:usingComparator:, and removed non-standard methods: - indexOfObject:sortedBySelector: - indexOfObject:sortedByFunction: - indexOfObject:sortedByDescriptors: - indexOfObject:sortedByFunction:context: Reviewed by me. --- AppKit/CPTableView.j | 8 +- Foundation/CPArray/CPArray.j | 172 +++++++----------------- Foundation/CPArray/CPMutableArray.j | 26 ++-- Foundation/CPArray/_CPJavaScriptArray.j | 3 +- Foundation/CPAttributedString.j | 2 +- Foundation/CPException.j | 15 +++ Tests/Foundation/CPArrayTest.j | 69 ++++------ 7 files changed, 113 insertions(+), 182 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index f5138c303..9f9015eb3 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -1714,7 +1714,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; { if (_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_) { - return idx = [_cachedRowHeights indexOfObject:aPoint sortedByFunction:function(aPoint, rowCache) { + return idx = [_cachedRowHeights indexOfObject:aPoint + inSortedRange:nil + options:0 + usingComparator:function(aPoint, rowCache) + { var upperBound = rowCache.heightAboveRow; if (aPoint.y < upperBound) @@ -1724,7 +1728,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; return CPOrderedDescending; return CPOrderedSame; - }]; + }]; } var y = aPoint.y, diff --git a/Foundation/CPArray/CPArray.j b/Foundation/CPArray/CPArray.j index 212c9935d..dc467c481 100755 --- a/Foundation/CPArray/CPArray.j +++ b/Foundation/CPArray/CPArray.j @@ -26,9 +26,15 @@ @import "CPRange.j" @import "CPSortDescriptor.j" -CPEnumerationNormal = 0; -CPEnumerationConcurrent = 1 << 0; -CPEnumerationReverse = 1 << 1; + +CPEnumerationNormal = 0; +CPEnumerationConcurrent = 1 << 0; +CPEnumerationReverse = 1 << 1; + +CPBinarySearchingFirstEqual = 1 << 8; +CPBinarySearchingLastEqual = 1 << 9; +CPBinarySearchingInsertionIndex = 1 << 10; + #define FORWARD_TO_CONCRETE_CLASS()\ if (self === _CPSharedPlaceholderArray)\ @@ -393,145 +399,67 @@ CPEnumerationReverse = 1 << 1; 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 +- (CPUInteger)indexOfObject:(id)anObject + inSortedRange:(CPRange)aRange + options:(CPBinarySearchingOptions)options + usingComparator:(Function)aComparator { - return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) - { - return objj_msgSend(lhs, aSelector, rhs); - }]; -} + // FIXME: comparator is not a function + if (!aComparator) + _CPRaiseInvalidArgumentException(self, _cmd, "comparator is 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: -
-    aFunction(anObject, currentObjectInArrayForComparison)
-    
- @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: -
-    aFunction(anObject, currentObjectInArrayForComparison, context)
-    
- @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. -*/ -- (CPUInteger)indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext -{ - if (!aFunction) - return CPNotFound; + if ((options & CPBinarySearchingFirstEqual) && (options & CPBinarySearchingLastEqual)) + _CPRaiseInvalidArgumentException(self, _cmd, + "both CPBinarySearchingFirstEqual and CPBinarySearchingLastEqual options cannot be specified"); var count = [self count]; if (count <= 0) - return CPNotFound; + return (options & CPBinarySearchingInsertionIndex) ? 0 : CPNotFound; - var mid, - c, - first = 0, - last = count - 1; + var first = aRange ? aRange.location : 0, + last = aRange ? CPMaxRange(aRange) : [self count] - 1; + + if (first < 0) + _CPRaiseRangeException(self, _cmd, first, count); + + if (last >= count) + _CPRaiseRangeException(self, _cmd, last, count); while (first <= last) { - mid = FLOOR((first + last) / 2); - c = aFunction(anObject, [self objectAtIndex:mid], aContext); + var middle = FLOOR((first + last) / 2), + result = aComparator(anObject, [self objectAtIndex:middle]); - if (c > 0) - first = mid + 1; + if (result > 0) + first = middle + 1; - else if (c < 0) - last = mid - 1; + else if (result < 0) + last = middle - 1; else { - while (mid < count - 1 && - aFunction(anObject, [self objectAtIndex:mid + 1], aContext) === CPOrderedSame) - mid++; + if (options & CPBinarySearchingFirstEqual) + while (middle++ < count - 1 && + aComparator(anObject, [self objectAtIndex:middle]) === CPOrderedSame); - return mid; + else if (options & CPBinarySearchingLastEqual) + { + while (middle-- > 0 && + aComparator(anObject, [self objectAtIndex:middle]) === CPOrderedSame); + + if (options & CPBinarySearchingInsertionIndex) + ++middle; + } + + return middle; } } - var result = -first - 1; + if (options & CPBinarySearchingInsertionIndex) + return MAX(first, 0); - if (result < 0) - return CPNotFound; - - return result; -} - -/*! - 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 index = 0, - result = CPOrderedSame; - - while (index < count && (result = [[descriptors objectAtIndex:index++] compareObject:lhs withObject:rhs]) === CPOrderedSame); - - 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; + return CPNotFound; } // Sending messages to elements diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j index 0220e0982..eb87ce9cc 100644 --- a/Foundation/CPArray/CPMutableArray.j +++ b/Foundation/CPArray/CPMutableArray.j @@ -90,31 +90,29 @@ - (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors { - var count = [descriptors count], - index; + var index, + count = [descriptors count]; if (count) - { - // FIXME: UGH - index = [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) + index = [self indexOfObject:anObject + inSortedRange:nil + options:CPBinarySearchingInsertionIndex + usingComparator: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)) + ++index; return result; - } context:nil]; + }]; - if (index < 0) - index = -index - 1; - } else - index = self.length; + index = [self count]; [self insertObject:anObject atIndex:index]; + return index; } diff --git a/Foundation/CPArray/_CPJavaScriptArray.j b/Foundation/CPArray/_CPJavaScriptArray.j index c8ee68b8a..21aa25769 100644 --- a/Foundation/CPArray/_CPJavaScriptArray.j +++ b/Foundation/CPArray/_CPJavaScriptArray.j @@ -101,8 +101,7 @@ - (id)objectAtIndex:(CPUInteger)anIndex { if (anIndex >= self.length || anIndex < 0) - [CPException raise:CPRangeException - reason:@"index (" + anIndex + @") beyond bounds (" + self.length + @")"]; + _CPRaiseRangeException(self, _cmd, anIndex, self.length); return self[anIndex]; } diff --git a/Foundation/CPAttributedString.j b/Foundation/CPAttributedString.j index bcbd9a732..96868717a 100644 --- a/Foundation/CPAttributedString.j +++ b/Foundation/CPAttributedString.j @@ -146,7 +146,7 @@ return CPOrderedAscending; } - return [_rangeEntries indexOfObject:anIndex sortedByFunction:sortFunction]; + return [_rangeEntries indexOfObject:anIndex inSortedRange:nil options:0 usingComparator:sortFunction]; } //Retrieving Attribute Information diff --git a/Foundation/CPException.j b/Foundation/CPException.j index 68b0d4279..9d9ca40f4 100755 --- a/Foundation/CPException.j +++ b/Foundation/CPException.j @@ -205,11 +205,26 @@ Error.prototype._userInfo = null; [CPException initialize]; +#define METHOD_CALL_STRING()\ + ((class_isMetaClass(anObject.isa) ? "+" : "-") + "[" + [anObject className] + " " + aSelector + "]: ") + function _CPRaiseInvalidAbstractInvocation(anObject, aSelector) { [CPException raise:CPInvalidArgumentException reason:@"*** -" + sel_getName(aSelector) + @" cannot be sent to an abstract object of class " + [anObject className] + @": Create a concrete instance!"]; } +function _CPRaiseInvalidArgumentException(anObject, aSelector, aMessage) +{ + [CPException raise:CPInvalidArgumentException + reason:METHOD_CALL_STRING() + aMessage]; +} + +function _CPRaiseRangeException(anObject, aSelector, anIndex, aCount) +{ + [CPException raise:CPRangeException + reason:METHOD_CALL_STRING() + "index (" + anIndex + ") beyond bounds (" + aCount + ")"]; +} + function _CPReportLenientDeprecation(/*Class*/ aClass, /*SEL*/ oldSelector, /*SEL*/ newSelector) { CPLog.warn("[" + CPStringFromClass(aClass) + " " + CPStringFromSelector(oldSelector) + "] is deprecated, using " + CPStringFromSelector(newSelector) + " instead."); diff --git a/Tests/Foundation/CPArrayTest.j b/Tests/Foundation/CPArrayTest.j index 0f4fa99aa..91c1f2522 100644 --- a/Tests/Foundation/CPArrayTest.j +++ b/Tests/Foundation/CPArrayTest.j @@ -38,8 +38,8 @@ [self assert:array equals:[@"one", @"two", @"three", @"four", @"a", @"b"]]; - var array = [CPMutableArray arrayWithObjects: @"one", @"two", @"three", @"four"], - newAdditions = [CPArray arrayWithObjects: @"a", @"b", @"c"], + var array = [CPMutableArray arrayWithObjects:@"one", @"two", @"three", @"four"], + newAdditions = [CPArray arrayWithObjects:@"a", @"b", @"c"], indexes = [CPMutableIndexSet indexSetWithIndex:1]; [indexes addIndex:2]; @@ -50,8 +50,8 @@ [self assert:array equals:[@"one", @"a", @"b", @"two", @"c", @"three", @"four"]]; - var array = [CPMutableArray arrayWithObjects: @"one", @"two", @"three", @"four"], - newAdditions = [CPArray arrayWithObjects: @"a", @"b", @"c"], + var array = [CPMutableArray arrayWithObjects:@"one", @"two", @"three", @"four"], + newAdditions = [CPArray arrayWithObjects:@"a", @"b", @"c"], indexes = [CPMutableIndexSet indexSetWithIndex:1]; [indexes addIndex:2]; @@ -140,25 +140,12 @@ [self assert:[array indexOfObjectWithOptions:CPEnumerationReverse passingTest:agePredicate context:7] equals:3]; } -- (void)testIndexOfObjectSortedByFunction +- (void)testIndexOfObject_inSortedRange_options_usingComparator_ { var array = [0, 1, 2, 3, 4, 7]; - [self assert:[array indexOfObject:3 sortedByFunction:function(a, b){ return a - b; }] equals:3]; - [self assert:[[array arrayByReversingArray] indexOfObject:3 sortedByFunction:function(a, b){ return b - a; }] equals:2]; -} - -- (void)testIndexOfObjectSortedByDescriptors -{ - var array = [0, 1, 2, 3, 4, 7]; - - [self assert:[array indexOfObject:3 - sortedByDescriptors:[[[CPSortDescriptor alloc] initWithKey:@"intValue" ascending:YES]]] - equals:3]; - - [self assert:[[array arrayByReversingArray] indexOfObject:3 - sortedByDescriptors:[[[CPSortDescriptor alloc] initWithKey:@"intValue" ascending:NO]]] - equals:2]; + [self assert:[array indexOfObject:3 inSortedRange:nil options:0 usingComparator:function(a, b){ return a - b; }] equals:3]; + [self assert:[[array arrayByReversingArray] indexOfObject:3 inSortedRange:nil options:0 usingComparator:function(a, b){ return a - b; }] equals:2]; } - (void)testIndexOutOfBounds @@ -171,7 +158,7 @@ catch (anException) { [self assert:[anException name] equals:CPRangeException]; - [self assert:[anException reason] equals:@"index (0) beyond bounds (0)"]; + [self assertTrue:[[anException reason] rangeOfString:(@"index (0) beyond bounds (0)")].location !== CPNotFound]; } [[0, 1, 2] objectAtIndex:0]; @@ -186,7 +173,7 @@ catch (anException) { [self assert:[anException name] equals:CPRangeException]; - [self assert:[anException reason] equals:@"index (3) beyond bounds (3)"]; + [self assertTrue:[[anException reason] rangeOfString:(@"index (3) beyond bounds (3)")].location !== CPNotFound]; } try @@ -197,7 +184,7 @@ catch (anException) { [self assert:[anException name] equals:CPRangeException]; - [self assert:[anException reason] equals:@"index (4) beyond bounds (3)"]; + [self assertTrue:[[anException reason] rangeOfString:(@"index (4) beyond bounds (3)")].location !== CPNotFound]; } } @@ -206,79 +193,79 @@ var descriptors = [[[CPSortDescriptor alloc] initWithKey:@"intValue" ascending:YES]]; var array = [1, 3, 5]; - [array insertObject: 0 inArraySortedByDescriptors:descriptors]; + [array insertObject:0 inArraySortedByDescriptors:descriptors]; [self assert:[0, 1, 3, 5] equals:array]; array = [1, 3, 5]; - [array insertObject: 2 inArraySortedByDescriptors:descriptors]; + [array insertObject:2 inArraySortedByDescriptors:descriptors]; [self assert:[1, 2, 3, 5] equals:array]; array = [1, 3, 5]; - [array insertObject: 1 inArraySortedByDescriptors:descriptors]; + [array insertObject:1 inArraySortedByDescriptors:descriptors]; [self assert:[1, 1, 3, 5] equals:array]; array = [1, 3, 5]; - [array insertObject: 6 inArraySortedByDescriptors:descriptors]; + [array insertObject:6 inArraySortedByDescriptors:descriptors]; [self assert:[1, 3, 5, 6] equals:array]; array = [1, 3, 5]; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[1, 3, 3, 5] equals:array]; array = []; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[3] equals:array]; descriptors = [[[CPSortDescriptor alloc] initWithKey:@"intValue" ascending:NO]]; array = [5, 3, 1]; - [array insertObject: 0 inArraySortedByDescriptors:descriptors]; + [array insertObject:0 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 1, 0] equals:array]; array = [5, 3, 1]; - [array insertObject: 2 inArraySortedByDescriptors:descriptors]; + [array insertObject:2 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 2, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 1 inArraySortedByDescriptors:descriptors]; + [array insertObject:1 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 1, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 6 inArraySortedByDescriptors:descriptors]; + [array insertObject:6 inArraySortedByDescriptors:descriptors]; [self assert:[6, 5, 3, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 3, 1] equals:array]; array = []; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[3] equals:array]; descriptors = [[[CPSortDescriptor alloc] initWithKey:@"intValue" ascending:NO]]; array = [5, 3, 1]; - [array insertObject: 0 inArraySortedByDescriptors:descriptors]; + [array insertObject:0 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 1, 0] equals:array]; array = [5, 3, 1]; - [array insertObject: 2 inArraySortedByDescriptors:descriptors]; + [array insertObject:2 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 2, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 1 inArraySortedByDescriptors:descriptors]; + [array insertObject:1 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 1, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 6 inArraySortedByDescriptors:descriptors]; + [array insertObject:6 inArraySortedByDescriptors:descriptors]; [self assert:[6, 5, 3, 1] equals:array]; array = [5, 3, 1]; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[5, 3, 3, 1] equals:array]; array = []; - [array insertObject: 3 inArraySortedByDescriptors:descriptors]; + [array insertObject:3 inArraySortedByDescriptors:descriptors]; [self assert:[3] equals:array]; }