mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-25 03:40:43 +00:00
improve _CPKVCArray
_CPKVCArray now consistently calls both single and multiple indexes accessors consistently Tests that verify the behavior are included
This commit is contained in:
+83
-14
@@ -131,7 +131,7 @@
|
||||
|
||||
_replaceManySEL = sel_getName(@"replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":");
|
||||
if ([_proxyObject respondsToSelector:_replaceManySEL])
|
||||
_replace = [_proxyObject methodForSelector:_replaceManySEL];
|
||||
_replaceMany = [_proxyObject methodForSelector:_replaceManySEL];
|
||||
|
||||
_objectAtIndexSEL = sel_getName(@"objectIn" + capitalizedKey + "AtIndex:");
|
||||
if ([_proxyObject respondsToSelector:_objectAtIndexSEL])
|
||||
@@ -233,10 +233,25 @@
|
||||
|
||||
- (id)objectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
if (_objectAtIndex)
|
||||
return _objectAtIndex(_proxyObject, _objectAtIndexSEL, anIndex);
|
||||
return [[self objectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]] firstObject];
|
||||
}
|
||||
|
||||
return [[self _representedObject] objectAtIndex:anIndex];
|
||||
- (CPArray)objectsAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
if (_objectsAtIndexes)
|
||||
return _objectsAtIndexes(_proxyObject, _objectsAtIndexesSEL, theIndexes);
|
||||
if (_objectAtIndex)
|
||||
{
|
||||
var index = CPNotFound,
|
||||
objects = [];
|
||||
|
||||
while ((index = [theIndexes indexGreaterThanIndex:index]) !== CPNotFound)
|
||||
objects.push(_objectAtIndex(_proxyObject, _objectAtIndexSEL, index));
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
return [[self _representedObject] objectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)addObject:(id)anObject
|
||||
@@ -288,6 +303,27 @@
|
||||
[self removeObject:anObject inRange:CPMakeRange(0, [self count])];
|
||||
}
|
||||
|
||||
- (void)removeObject:(id)theObject inRange:(CPRange)theRange
|
||||
{
|
||||
if (_remove)
|
||||
_remove(_proxyObject, _removeSEL, [self indexOfObject:theObject inRange:theRange]);
|
||||
else if (_removeMany)
|
||||
{
|
||||
var index = [self indexOfObject:theObject inRange:theRange];
|
||||
_removeMany(_proxyObject, _removeManySEL, [CPIndexSet indexSetWithIndex:index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var index;
|
||||
|
||||
while ((index = [self indexOfObject:theObject inRange:theRange]) !== CPNotFound)
|
||||
{
|
||||
[self removeObjectAtIndex:index];
|
||||
theRange = CPIntersectionRange(CPMakeRange(index, length - index), theRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeLastObject
|
||||
{
|
||||
if (_remove)
|
||||
@@ -301,24 +337,57 @@
|
||||
|
||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
if (_remove)
|
||||
return _remove(_proxyObject, _removeSEL, anIndex);
|
||||
[self removeObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
|
||||
}
|
||||
|
||||
var target = [[self _representedObject] copy];
|
||||
- (void)removeObjectsAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
if (_removeMany)
|
||||
_removeMany(_proxyObject, _removeManySEL, theIndexes);
|
||||
else if (_remove)
|
||||
{
|
||||
var index = [theIndexes lastIndex];
|
||||
|
||||
[target removeObjectAtIndex:anIndex];
|
||||
[self _setRepresentedObject:target];
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
_remove(_proxyObject, _removeSEL, index)
|
||||
index = [theIndexes indexLessThanIndex:index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = [[self _representedObject] copy];
|
||||
[target removeObjectsAtIndexes:theIndexes];
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
|
||||
{
|
||||
if (_replace)
|
||||
return _replace(_proxyObject, _replaceSEL, anIndex, anObject);
|
||||
[self replaceObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] withObjects:[anObject]]
|
||||
}
|
||||
|
||||
var target = [[self _representedObject] copy];
|
||||
- (void)replaceObjectsAtIndexes:(CPIndexSet)theIndexes withObjects:(CPArray)theObjects
|
||||
{
|
||||
if (_replaceMany)
|
||||
return _replaceMany(_proxyObject, _replaceManySEL, theIndexes, theObjects);
|
||||
else if (_replace)
|
||||
{
|
||||
var i = 0,
|
||||
index = [theIndexes firstIndex];
|
||||
|
||||
[target replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
[self _setRepresentedObject:target];
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
_replace(_proxyObject, _replaceSEL, index, [theObjects objectAtIndex:i++]);
|
||||
index = [theIndexes indexGreaterThanIndex:index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = [[self _representedObject] copy];
|
||||
[target replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
var COUNTER;
|
||||
|
||||
@implementation CPKVCArrayTest : OJTestCase
|
||||
{
|
||||
TestObject _object @accessors(property=object);
|
||||
ImplementedTestObject _implementedObject @accessors(property=implementedObject);
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
_object = [[TestObject alloc] init];
|
||||
_implementedObject = [[ImplementedTestObject alloc] init];
|
||||
|
||||
COUNTER = 0;
|
||||
}
|
||||
|
||||
- (void)_patchSelector:(SEL)theSelector
|
||||
{
|
||||
var method = class_getInstanceMethod([[self implementedObject] class], theSelector),
|
||||
implementation = method_getImplementation(method);
|
||||
|
||||
class_addMethod([[self object] class], theSelector, implementation);
|
||||
}
|
||||
|
||||
- (void)testUsesCountOfKey
|
||||
{
|
||||
[self _patchSelector:@selector(countOfValues)];
|
||||
|
||||
var count = [[[self object] mutableArrayValueForKey:@"values"] count];
|
||||
|
||||
[self assert:10 equals:count message:@"countOfValues should return 10"];
|
||||
[self assert:1 equals:COUNTER message:@"countOfValues should have been called once"]
|
||||
}
|
||||
|
||||
- (void)testObjectAtIndexUsesObjectInKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(objectInValuesAtIndex:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
object = [values objectAtIndex:0];
|
||||
|
||||
[self assert:0 equals:object]
|
||||
[self assert:1 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectsAtIndexesUsesObjectInKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(objectInValuesAtIndex:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
|
||||
|
||||
[self assert:[0, 1] equals:objects]
|
||||
[self assert:2 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectAtIndexUsesKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(valuesAtIndexes:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
object = [values objectAtIndex:0];
|
||||
|
||||
[self assert:0 equals:object]
|
||||
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectsAtIndexesUsesKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(valuesAtIndexes:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
|
||||
|
||||
[self assert:[0, 1] equals:objects]
|
||||
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
|
||||
|
||||
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
|
||||
[self assert:1 equals:COUNTER message:@"insertObject:inValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
|
||||
|
||||
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
|
||||
[self assert:2
|
||||
equals:COUNTER
|
||||
message:@"insertValues:atIndexes: should have been called once for each object"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(insertValues:atIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
|
||||
|
||||
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
|
||||
[self assert:1 equals:COUNTER message:@"insertValues:atIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(insertValues:atIndexes:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
|
||||
|
||||
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"insertValues:atIndexes: should have been called once for each object"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectAtIndexUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
|
||||
|
||||
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeObjectFromValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectAtIndexesUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
|
||||
|
||||
[self assert:[2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:2 equals:COUNTER message:@"removeValuesAtIndex: should have been called once for each object"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
|
||||
|
||||
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"removeValuesAtIndexes: should have been once for each object"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(3, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
|
||||
|
||||
[self assert:[0, 1, 2, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceObjectInKeyAtIndexWithObject
|
||||
{
|
||||
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:0 withObject:1];
|
||||
|
||||
[self assert:[1, 1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceObjectInKeyAtIndexWithObject
|
||||
{
|
||||
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(5, 3)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[1, 2, 3]];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 1, 2, 3, 8, 9] equals:[[self object] values]];
|
||||
[self assert:3
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceKeyAtIndexesWithKeys
|
||||
{
|
||||
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:5 withObject:6];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 6, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceKeyAtIndexesWithKeys
|
||||
{
|
||||
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(6, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[7, 8]];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 5, 7, 8, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndexes:withValues: should have been called once"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TestObject : CPObject
|
||||
{
|
||||
CPArray _values @accessors(property=values);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ImplementedTestObject : TestObject
|
||||
{
|
||||
}
|
||||
|
||||
- (int)countOfValues
|
||||
{
|
||||
// CPLog.warn(@"countOfValues");
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] count];
|
||||
}
|
||||
|
||||
- (id)objectInValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"objectInValuesAtIndex: %@", theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] objectAtIndex:theIndex];
|
||||
}
|
||||
|
||||
- (CPArray)valuesAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"valuesAtIndexes: %@", theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] objectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)theObject inValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"insertObject: %@ inValuesAtIndex: %@", theObject, theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] insertObject:theObject atIndex:theIndex];
|
||||
}
|
||||
|
||||
- (void)insertValues:(CPArray)theObjects atIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"insertValues: %@ atIndexes: %@", theObjects, theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] insertObjects:theObjects atIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"removeObjectFromValuesAtIndex: %@", theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] removeObjectAtIndex:theIndex];
|
||||
}
|
||||
|
||||
- (void)removeValuesAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"removeValuesAtIndexes: %@", theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] removeObjectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)replaceObjectInValuesAtIndex:(int)theIndex withObject:(id)theObject
|
||||
{
|
||||
// CPLog.warn(@"replaceObjectInValuesAtIndex: %@ withObject: %@", theIndex, theObject);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] replaceObjectAtIndex:theIndex withObject:theObject];
|
||||
}
|
||||
|
||||
- (void)replaceValuesAtIndexes:(CPIndexSet)theIndexes withValues:(id)theObjects
|
||||
{
|
||||
// CPLog.warn(@"replaceValuesAtIndexes: %@ withValues: %@", theIndexes, theObjects);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user