Collection KVC fixes, nextObject correctness

- Moved common CPArray/CPSet collection KVC operators to _CPCollectionKVCOperators.
- Implemented KVC operator dispatch using Objective-J.
- Fixed infinite loop with empty  collection in @min, @max and @sum operators.
- Correctly return valueForUndefinedKey when necessary.
- valueForUndefinedKey reason uses raw description for consistency, class' overridden description may not helpful at all.
- Don't create a forwarder for @ operators with property paths.
- Fixed CPSet -valueForKeyPath to correctly deal with nil/undefined/empty values.
- Added tests for collection  KVC operators.
- enumerator -nextObject should always compare against nil for clarity, correctness, and consistency.
This commit is contained in:
Aparajita Fishman committed 2012-04-03 22:16:55 -07:00
1 parent 029e45d027
commit e2ead56256
30 files changed
+265 -272

No files matched your search

+5 -5
View File
@@ -104,10 +104,10 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
- (void)setFlashVars:(CPDictionary)aDictionary
{
var varString = @"",
enumerator = [aDictionary keyEnumerator];
enumerator = [aDictionary keyEnumerator],
key;
var key;
while (key = [enumerator nextObject])
while ((key = [enumerator nextObject]) !== nil)
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
if (!_params)
@@ -146,7 +146,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
var enumerator = [_params keyEnumerator],
key;
while (_DOMObjectElement && (key = [enumerator nextObject]))
while (_DOMObjectElement && (key = [enumerator nextObject]) !== nil)
{
var param = document.createElement(@"param");
param.name = key;
@@ -178,7 +178,7 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
paramEnumerator = [_params keyEnumerator],
key;
while (key = [paramEnumerator nextObject])
while ((key = [paramEnumerator nextObject]) !== nil)
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
_DOMObjectElement = document.createElement(@"object");
+2 -2
View File
@@ -473,7 +473,7 @@ var STICKY_TIME_INTERVAL = 500,
var iter = [selectorNames objectEnumerator],
obj;
while (obj = [iter nextObject])
while ((obj = [iter nextObject]) !== nil)
{
var aSelector = CPSelectorFromString(obj);
@@ -504,7 +504,7 @@ var STICKY_TIME_INTERVAL = 500,
var iter = [[menu itemArray] objectEnumerator],
obj;
while (obj = [iter nextObject])
while ((obj = [iter nextObject]) !== nil)
{
if ([obj isHidden] || ![obj isEnabled])
continue;
+1 -1
View File
@@ -2689,7 +2689,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
oldMainSortDescriptor = [[self sortDescriptors] objectAtIndex: 0];
// Remove every main descriptor equivalents (normally only one)
while ((descriptor = [e nextObject]) != nil)
while ((descriptor = [e nextObject]) !== nil)
{
if ([[descriptor key] isEqual: [newMainSortDescriptor key]])
[outdatedDescriptors addObject:descriptor];
+1 -1
View File
@@ -255,7 +255,7 @@ var CPThemesByName = { },
attributeNames = [attributes keyEnumerator],
objectThemeClass = [anObject themeClass];
while (attributeName = [attributeNames nextObject])
while ((attributeName = [attributeNames nextObject]) !== nil)
[self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
}
+1 -1
View File
@@ -534,7 +534,7 @@ var CPViewFlags = { },
var addedSubview = nil,
addedSubviewEnumerator = [addedSubviews objectEnumerator];
while (addedSubview = [addedSubviewEnumerator nextObject])
while ((addedSubview = [addedSubviewEnumerator nextObject]) !== nil)
[self addSubview:addedSubview];
// If the order is fine, no need to reorder.
+1 -1
View File
@@ -137,7 +137,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
var key = nil,
keyEnumerator = [replacementClasses keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
}
+1 -1
View File
@@ -106,7 +106,7 @@
var object = nil,
objectEnumerator = [_visibleWindows objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
[_replacementObjects[[object UID]] makeKeyAndOrderFront:self];
}
+1 -1
View File
@@ -1625,7 +1625,7 @@ function CPWindowObjectList()
platformWindow = nil,
windowObjects = [];
while (platformWindow = [platformWindowEnumerator nextObject])
while ((platformWindow = [platformWindowEnumerator nextObject]) !== nil)
{
var levels = platformWindow._windowLevels,
layers = platformWindow._windowLayers,
+1 -1
View File
@@ -49,7 +49,7 @@ function findCibClassDependencies(cibPath) {
var key = nil,
keyEnumerator = [replacementClasses keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
}
+12 -100
View File
@@ -22,6 +22,7 @@
@import "CPArray.j"
@import "CPNull.j"
@import "_CPCollectionKVCOperators.j"
@implementation CPObject (CPArrayKVO)
@@ -240,6 +241,7 @@
{
if (_objectsAtIndexes)
return _objectsAtIndexes(_proxyObject, _objectsAtIndexesSEL, theIndexes);
if (_objectAtIndex)
{
var index = CPNotFound,
@@ -413,9 +415,9 @@
@end
//KVC on CPArray objects act on each item of the array, rather than on the array itself
// KVC on CPArray objects act on each item of the array, rather than on the array itself
@implementation CPArray (KeyValueCoding)
@implementation CPArray (CPKeyValueCoding)
- (id)valueForKey:(CPString)aKey
{
@@ -424,10 +426,10 @@
if (aKey.indexOf(".") !== -1)
[CPException raise:CPInvalidArgumentException reason:"called valueForKey: on an array with a complex key ("+aKey+"). use valueForKeyPath:"];
if (aKey == "@count")
if (aKey === "@count")
return length;
return nil;
return [self valueForUndefinedKey:aKey];
}
else
{
@@ -451,6 +453,9 @@
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (!aKeyPath)
[self valueForUndefinedKey:@"<empty path>"];
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
@@ -465,10 +470,7 @@
else
operator = aKeyPath.substring(1);
if (kvoOperators[operator])
return kvoOperators[operator](self, _cmd, parameter);
return nil;
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
@@ -495,7 +497,7 @@
var enumerator = [self objectEnumerator],
object;
while (object = [enumerator nextObject])
while ((object = [enumerator nextObject]) !== nil)
[object setValue:aValue forKey:aKey];
}
@@ -504,86 +506,12 @@
var enumerator = [self objectEnumerator],
object;
while (object = [enumerator nextObject])
while ((object = [enumerator nextObject]) !== nil)
[object setValue:aValue forKeyPath:aKeyPath];
}
@end
var kvoOperators = [];
// HACK: prevent these from becoming globals. workaround for obj-j "function foo(){}" behavior
var avgOperator,
maxOperator,
minOperator,
countOperator,
sumOperator;
kvoOperators["avg"] = function avgOperator(self, _cmd, param)
{
var objects = [self valueForKeyPath:param],
length = [objects count],
index = length,
average = 0.0;
if (!length)
return 0;
while (index--)
average += [objects[index] doubleValue];
return average / length;
}
kvoOperators["max"] = function maxOperator(self, _cmd, param)
{
var objects = [self valueForKeyPath:param],
index = [objects count] - 1,
max = [objects lastObject];
while (index--)
{
var item = objects[index];
if ([max compare:item] < 0)
max = item;
}
return max;
}
kvoOperators["min"] = function minOperator(self, _cmd, param)
{
var objects = [self valueForKeyPath:param],
index = [objects count] - 1,
min = [objects lastObject];
while (index--)
{
var item = objects[index];
if ([min compare:item] > 0)
min = item;
}
return min;
}
kvoOperators["count"] = function countOperator(self, _cmd, param)
{
return [self count];
}
kvoOperators["sum"] = function sumOperator(self, _cmd, param)
{
var objects = [self valueForKeyPath:param],
index = [objects count],
sum = 0.0;
while (index--)
sum += [objects[index] doubleValue];
return sum;
}
@implementation CPArray (KeyValueObserving)
- (void)addObserver:(id)anObserver toObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
@@ -610,20 +538,4 @@ kvoOperators["sum"] = function sumOperator(self, _cmd, param)
}
}
- (void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
{
if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd])
[CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"];
else
[super addObserver:observer forKeyPath:aKeyPath options:options context:context];
}
- (void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath
{
if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd])
[CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"];
else
[super removeObserver:observer forKeyPath:aKeyPath];
}
@end
+1 -1
View File
@@ -241,7 +241,7 @@ var CPCharacterSetInvertedKey = @"CPCharacterSetInvertedKey";
var enu = [_ranges objectEnumerator],
range;
while (range = [enu nextObject])
while ((range = [enu nextObject]) !== nil)
{
if (CPLocationInRange(c, range))
return !_inverted;
+1 -1
View File
@@ -50,7 +50,7 @@
{
var key = [_keyEnumerator nextObject];
if (!key)
if (key === nil)
return nil;
return [_dictionary objectForKey:key];
+8 -3
View File
@@ -157,7 +157,7 @@ var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
- (id)valueForUndefinedKey:(CPString)aKey
{
[[CPException exceptionWithName:CPUndefinedKeyException
reason:[self description] + " is not key value coding-compliant for the key " + aKey
reason:[self _objectDescription] + " is not key value coding-compliant for the key " + aKey
userInfo:[CPDictionary dictionaryWithObjects:[self, aKey] forKeys:[CPTargetObjectUserInfoKey, CPUnknownUserInfoKey]]] raise];
}
@@ -235,7 +235,7 @@ var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
key,
keyEnumerator = [keyedValues keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
value = [keyedValues objectForKey: key];
@@ -250,10 +250,15 @@ var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
- (void)setValue:(id)aValue forUndefinedKey:(CPString)aKey
{
[[CPException exceptionWithName:CPUndefinedKeyException
reason:[self description] + " is not key value coding-compliant for the key " + aKey
reason:[self _objectDescription] + " is not key value coding-compliant for the key " + aKey
userInfo:[CPDictionary dictionaryWithObjects:[self, aKey] forKeys:[CPTargetObjectUserInfoKey, CPUnknownUserInfoKey]]] raise];
}
- (CPString)_objectDescription
{
return "<" + [self className] + " 0x" + [CPString stringWithHash:[self UID]] + ">";
}
@end
@implementation CPDictionary (CPKeyValueCoding)
+2 -2
View File
@@ -664,7 +664,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
var forwarder = nil;
if (aPath.indexOf('.') != CPNotFound)
if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@')
forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext];
else
[self _replaceModifiersForKey:aPath];
@@ -1092,7 +1092,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
var dotIndex = aKeyPath.indexOf('.');
if (dotIndex == CPNotFound)
if (dotIndex === CPNotFound)
[CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: "+aKeyPath];
_firstPart = aKeyPath.substring(0, dotIndex);
+1 -1
View File
@@ -398,7 +398,7 @@ var _CPKeyedArchiverStringClass = Nil,
keys = [aDictionary keyEnumerator],
references = [CPDictionary dictionary];
while (key = [keys nextObject])
while ((key = [keys nextObject]) !== nil)
[references setObject:_CPKeyedArchiverEncodeObject(self, [aDictionary objectForKey:key], NO) forKey:key];
[_plistObject setObject:references forKey:aKey];
+3 -3
View File
@@ -103,7 +103,7 @@ var CPNotificationDefaultCenter = nil;
var name = nil,
names = [_namedRegistries keyEnumerator];
while (name = [names nextObject])
while ((name = [names nextObject]) !== nil)
[[_namedRegistries objectForKey:name] removeObserver:anObserver object:nil];
[_unnamedRegistry removeObserver:anObserver object:nil];
@@ -122,7 +122,7 @@ var CPNotificationDefaultCenter = nil;
var name = nil,
names = [_namedRegistries keyEnumerator];
while (name = [names nextObject])
while ((name = [names nextObject]) !== nil)
[[_namedRegistries objectForKey:name] removeObserver:anObserver object:anObject];
[_unnamedRegistry removeObserver:anObserver object:anObject];
@@ -225,7 +225,7 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
keys = [_objectObservers keyEnumerator];
// Iterate through every set of observers
while (key = [keys nextObject])
while ((key = [keys nextObject]) !== nil)
{
var observers = [_objectObservers objectForKey:key],
observer = nil,
@@ -512,9 +512,10 @@ var CPComparisonPredicateModifier,
result = (_modifier == CPAllPredicateModifier),
value;
while (value = [e nextObject])
while ((value = [e nextObject]) !== nil)
{
var eval = [self _evaluateValue:value rightValue:rightValue];
if (eval != result)
return eval;
}
@@ -59,7 +59,7 @@
collection = [_aggregate objectEnumerator],
exp;
while (exp = [collection nextObject])
while ((exp = [collection nextObject]) !== nil)
{
var eval = [exp expressionValueWithObject:object context:context];
[eval_array addObject:eval];
+26 -114
View File
@@ -23,6 +23,7 @@
@import "CPException.j"
@import "CPObject.j"
@import "CPSet.j"
@import "_CPCollectionKVCOperators.j"
@implementation CPObject (CPSetKVO)
@@ -218,7 +219,8 @@
{
var object,
objectEnumerator = [objects objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
_add(_proxyObject, _addSEL, object);
}
else
@@ -237,7 +239,8 @@
{
var object,
objectEnumerator = [aSet objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
_add(_proxyObject, _addSEL, object);
}
else
@@ -273,7 +276,8 @@
{
var object,
objectEnumerator = [aSet objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
_remove(_proxyObject, _removeSEL, object);
}
else
@@ -295,7 +299,8 @@
{
var object,
objectEnumerator = [objects objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
_remove(_proxyObject, _removeSEL, object);
}
else
@@ -317,7 +322,8 @@
{
var object,
objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
while (object = [objectEnumerator nextObject])
while ((object = [objectEnumerator nextObject]) !== nil)
_remove(_proxyObject, _removeSEL, object);
}
else
@@ -381,7 +387,10 @@
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (aKeyPath.indexOf("@") === 0)
if (!aKeyPath)
[self valueForUndefinedKey:@"<empty path>"];
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
@@ -395,10 +404,7 @@
else
operator = aKeyPath.substring(1);
if (kvoOperators[operator])
return kvoOperators[operator](self, _cmd, parameter);
return nil;
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
@@ -406,12 +412,17 @@
containedObject,
containedObjectValue,
containedObjectEnumerator = [self objectEnumerator];
while (containedObject = [containedObjectEnumerator nextObject])
while ((containedObject = [containedObjectEnumerator nextObject]) !== nil)
{
containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
if (containedObjectValue)
[valuesForKeySet addObject:containedObjectValue];
if (containedObjectValue === nil || containedObjectValue === undefined)
containedObjectValue = [CPNull null];
[valuesForKeySet addObject:containedObjectValue];
}
return valuesForKeySet;
}
}
@@ -420,108 +431,9 @@
{
var containedObject,
containedObjectEnumerator = [self objectEnumerator];
while (containedObject = [containedObjectEnumerator nextObject])
while ((containedObject = [containedObjectEnumerator nextObject]) !== nil)
[containedObject setValue:aValue forKey:aKey];
}
@end
var kvoOperators = [];
// FIXME: this is not DRY because the implementation in CPArray+KVO is merely the same!
// HACK: prevent these from becoming globals. workaround for obj-j "function foo(){}" behavior
var avgOperator,
maxOperator,
minOperator,
countOperator,
sumOperator;
kvoOperators["avg"] = function avgOperator(self, _cmd, param)
{
//CPSet returns a CPSet - to obtain an array call allObjects
var objects = [[self valueForKeyPath:param] allObjects],
length = [objects count],
index = length,
average = 0.0;
if (!length)
return 0;
while (index--)
average += [objects[index] doubleValue];
return average / length;
}
kvoOperators["max"] = function maxOperator(self, _cmd, param)
{
//CPSet returns a CPSet - to obtain an array call allObjects
var objects = [[self valueForKeyPath:param] allObjects],
index = [objects count] - 1,
max = [objects lastObject];
while (index--)
{
var item = objects[index];
if ([max compare:item] < 0)
max = item;
}
return max;
}
kvoOperators["min"] = function minOperator(self, _cmd, param)
{
//CPSet returns a CPSet - to obtain an array call allObjects
var objects = [[self valueForKeyPath:param] allObjects],
index = [objects count] - 1,
min = [objects lastObject];
while (index--)
{
var item = objects[index];
if ([min compare:item] > 0)
min = item;
}
return min;
}
kvoOperators["count"] = function countOperator(self, _cmd, param)
{
return [self count];
}
kvoOperators["sum"] = function sumOperator(self, _cmd, param)
{
//CPSet returns a CPSet - to obtain an array call allObjects
var objects = [[self valueForKeyPath:param] allObjects],
index = [objects count],
sum = 0.0;
while (index--)
sum += [objects[index] doubleValue];
return sum;
}
@implementation CPSet (CPKeyValueObserving)
- (void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
{
if ([isa instanceMethodForSelector:_cmd] === [CPSet instanceMethodForSelector:_cmd])
[CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPSet"];
else
[super addObserver:observer forKeyPath:aKeyPath options:options context:context];
}
- (void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath
{
if ([isa instanceMethodForSelector:_cmd] === [CPSet instanceMethodForSelector:_cmd])
[CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPSet"];
else
[super removeObserver:observer forKeyPath:aKeyPath];
}
@end
+2 -2
View File
@@ -106,7 +106,7 @@ var CPURLConnectionDelegate = nil;
key = nil,
keys = [fields keyEnumerator];
while (key = [keys nextObject])
while ((key = [keys nextObject]) !== nil)
request.setRequestHeader(key, [fields objectForKey:key]);
request.send([aRequest HTTPBody]);
@@ -196,7 +196,7 @@ var CPURLConnectionDelegate = nil;
key = nil,
keys = [fields keyEnumerator];
while (key = [keys nextObject])
while ((key = [keys nextObject]) !== nil)
_HTTPRequest.setRequestHeader(key, [fields objectForKey:key]);
_HTTPRequest.send([_request HTTPBody]);
+1 -1
View File
@@ -84,7 +84,7 @@ CPWebDAVManagerNonCollectionResourceType = 0;
URLString = nil,
URLStrings = [response keyEnumerator];
while (URLString = [URLStrings nextObject])
while ((URLString = [URLStrings nextObject]) !== nil)
{
var URL = [CPURL URLWithString:URLString],
properties = [response objectForKey:URLString];
+118
View File
@@ -0,0 +1,118 @@
/*
* _CPCollectionKVCOperators.j
* Foundation
*
* Created by Aparajita Fishman.
* Copyright 2012 Cappuccino Foundation.
*
* 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
*/
@implementation _CPCollectionKVCOperator : CPObject
+ (id)performOperation:(CPString)operator withCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
var selector = CPSelectorFromString(operator + @"ForCollection:propertyPath:");
if (![self respondsToSelector:selector])
return [aCollection valueForUndefinedKey:@"@" + operator];
return [self performSelector:selector withObject:aCollection withObject:propertyPath];
}
+ (double)avgForCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
if (!propertyPath)
return [aCollection valueForUndefinedKey:@"@avg"];
var objects = [aCollection valueForKeyPath:propertyPath],
average = 0.0,
enumerator = [objects objectEnumerator],
object;
while ((object = [enumerator nextObject]) !== nil)
average += [object doubleValue];
return average / [objects count];
}
+ (double)minForCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
if (!propertyPath)
return [aCollection valueForUndefinedKey:@"@min"];
var objects = [aCollection valueForKeyPath:propertyPath];
if ([objects count] === 0)
return nil;
var enumerator = [objects objectEnumerator],
min = [enumerator nextObject],
object;
while ((object = [enumerator nextObject]) !== nil)
{
if ([min compare:object] > 0)
min = object;
}
return min;
}
+ (double)maxForCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
if (!propertyPath)
return [aCollection valueForUndefinedKey:@"@max"];
var objects = [aCollection valueForKeyPath:propertyPath];
if ([objects count] === 0)
return nil;
var enumerator = [objects objectEnumerator],
max = [enumerator nextObject],
object;
while ((object = [enumerator nextObject]) !== nil)
{
if ([max compare:object] < 0)
max = object;
}
return max;
}
+ (double)sumForCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
if (!propertyPath)
return [aCollection valueForUndefinedKey:@"@sum"];
var objects = [aCollection valueForKeyPath:propertyPath],
sum = 0.0,
enumerator = [objects objectEnumerator],
object;
while ((object = [enumerator nextObject]) !== nil)
sum += [object doubleValue];
return sum;
}
+ (int)countForCollection:(id)aCollection propertyPath:(CPString)propertyPath
{
return [aCollection count];
}
@end
+39 -4
View File
@@ -2,8 +2,9 @@ var COUNTER;
@implementation CPKVCArrayTest : OJTestCase
{
TestObject _object @accessors(property=object);
ImplementedTestObject _implementedObject @accessors(property=implementedObject);
TestObject _object @accessors(property=object);
ImplementedTestObject _implementedObject @accessors(property=implementedObject);
CPArray _kvcArray @accessors(property=kvcArray);
}
- (void)setUp
@@ -298,11 +299,45 @@ var COUNTER;
message:@"replaceObjectInValuesAtIndexes:withValues: should have been called once"];
}
- (void)testKVCArrayOperators
{
var one = [1, 1, 1, 1, 1, 1, 1, 1],
two = [1, 2, 3, 4, 8, 0];
[self assert:[one valueForKey:"@count"] equals:8];
[self assert:[one valueForKeyPath:"@sum.intValue"] equals:8];
[self assert:[two valueForKeyPath:"@avg.intValue"] equals:3];
[self assert:[two valueForKeyPath:"@max.intValue"] equals:8];
[self assert:[two valueForKeyPath:"@min.intValue"] equals:0];
var a = [A new];
[a setValue:one forKey:"b"];
[self assert:[a valueForKeyPath:"b.@count"] equals:8];
[self assert:[a valueForKeyPath:"b.@sum.intValue"] equals:8];
var b = [];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Tom", 27] forKeys:[@"name", @"age"]]];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Dick", 31] forKeys:[@"name", @"age"]]];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Harry", 47] forKeys:[@"name", @"age"]]];
[self assert:[b valueForKeyPath:@"@sum.age"] equals:105];
[self assert:[b valueForKeyPath:@"@avg.age"] equals:35];
[self assert:[b valueForKeyPath:@"@min.age"] equals:27];
[self assert:[b valueForKeyPath:@"@max.age"] equals:47];
[self assert:[b valueForKeyPath:@"@min.name"] equals:@"Dick"];
[self assert:[b valueForKeyPath:@"@max.name"] equals:@"Tom"];
}
@end
@implementation A : CPObject
{
id b;
}
@end
@implementation TestObject : CPObject
{
CPArray _values @accessors(property=values);
CPArray _values @accessors(property=values);
}
- (id)init
@@ -393,4 +428,4 @@ var COUNTER;
[[self values] replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
}
@end
@end
-17
View File
@@ -382,23 +382,6 @@
[self assertTrue: _sawObservation message:"Never recieved an observation"];
}
- (void)testKVCArrayOperators
{
var one = [1, 1, 1, 1, 1, 1, 1, 1],
two = [1, 2, 3, 4, 8, 0];
[self assert:[one valueForKey:"@count"] equals:8];
[self assert:[one valueForKeyPath:"@sum.intValue"] equals:8];
[self assert:[two valueForKeyPath:"@avg.intValue"] equals:3];
[self assert:[two valueForKeyPath:"@max.intValue"] equals:8];
[self assert:[two valueForKeyPath:"@min.intValue"] equals:0];
var a = [A new];
[a setValue:one forKey:"b"];
[self assert:[a valueForKeyPath:"b.@count"] equals:8];
[self assert:[a valueForKeyPath:"b.@sum.intValue"] equals:8];
}
- (void)testPerformance
{
bob = [PersonTester new];
+4 -3
View File
@@ -232,7 +232,7 @@ var accessIVARS = YES;
value,
keyEnumerator = [dictForKeys keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
value = [dictForKeys objectForKey: key];
[self assert: [kvcTestObject valueForKey: key] same: value];
@@ -384,7 +384,8 @@ var accessIVARS = YES;
var key,
aValue,
keyEnumerator = [dictForKeys keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
aValue = [dictForKeys objectForKey: key];
[self assert: [kvcTestObject valueForKey: key] same: aValue];
@@ -417,7 +418,7 @@ var accessIVARS = YES;
value,
keyEnumerator = [dictForKeys keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
value = [dictForKeys objectForKey: key];
[self assert: [CPNull null] same:value ];
+26
View File
@@ -235,4 +235,30 @@
[self assertSet:set onlyHasObjects:["horizon", "surfer", "7"]];
}
- (void)testKVCSetOperators
{
var one = [CPSet setWithArray:[@"one", @"two", @"three"]],
two = [CPSet setWithArray:[1, 2, 3, 4, 8, 0]];
[self assert:[one valueForKey:"@count"] equals:3];
[self assert:[two valueForKey:"@count"] equals:6];
[self assert:[two valueForKeyPath:"@sum.intValue"] equals:18];
[self assert:[two valueForKeyPath:"@avg.doubleValue"] equals:3.0];
[self assert:[one valueForKeyPath:"@max.description"] equals:@"two"];
[self assert:[two valueForKeyPath:"@max.intValue"] equals:8];
[self assert:[one valueForKeyPath:"@min.description"] equals:@"one"];
[self assert:[two valueForKeyPath:"@min.intValue"] equals:0];
var b = [CPSet set];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Tom", 27] forKeys:[@"name", @"age"]]];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Dick", 31] forKeys:[@"name", @"age"]]];
[b addObject:[CPDictionary dictionaryWithObjects:[@"Harry", 47] forKeys:[@"name", @"age"]]];
[self assert:[b valueForKeyPath:@"@sum.age"] equals:105];
[self assert:[b valueForKeyPath:@"@avg.age"] equals:35];
[self assert:[b valueForKeyPath:@"@min.age"] equals:27];
[self assert:[b valueForKeyPath:@"@max.age"] equals:47];
[self assert:[b valueForKeyPath:@"@min.name"] equals:@"Dick"];
[self assert:[b valueForKeyPath:@"@max.name"] equals:@"Tom"];
}
@end
+1 -1
View File
@@ -189,7 +189,7 @@ function config(/*va_args*/)
var key = nil,
keyEnumerator = [configuration storedKeyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
print(key + '=' + [configuration valueForKey:key]);
}
else if (action === "get")
+1 -1
View File
@@ -166,7 +166,7 @@ function gen(/*va_args*/)
key = null,
keyEnumerator = [configuration keyEnumerator];
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
contents = contents.replace(new RegExp("__" + RegExp.escape(key) + "__", 'g'), [configuration valueForKey:key]);
FILE.write(path, contents, { charset: "UTF-8"});
+1 -1
View File
@@ -133,7 +133,7 @@ var NSTransformers = [CPSet setWithObjects:
keyEnumerator = [NSOptions keyEnumerator],
key;
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
var CPKey = @"CP" + key.substring(2),
NSValue = [NSOptions objectForKey:key];
+1 -1
View File
@@ -45,7 +45,7 @@
// Some of the item identifiers will be changed when loaded by NSToolbarItem, so we must change
// the map to correspond.
while (key = [keyEnumerator nextObject])
while ((key = [keyEnumerator nextObject]) !== nil)
{
var transformedKey = NS_CPToolbarItemIdentifierMap[key] || key;
[_identifiedItems setObject:[nsIdentifiedItems objectForKey:key] forKey:transformedKey];