Deconstruct _CPSet monolithic layout and relocate KVO proxies

•	Refactor @implementation _CPKVCSet and CPObject (CPSetKVO) from _CPSet.j to the tail of CPMutableSet.j.
•	Fix legacy compiler parse failures by ensuring the CPMutableSet superclass symbol is fully resolved within the translation unit prior to proxy evaluation.
•	Maintain strict DAG compliance across the modern toolchain pipeline topology.
•	Embed explicit #pragma mark navigation landmarks and enforce Allman brace layout to improve human structural ergonomics.
This commit is contained in:
David Richardson
2026-07-08 17:35:33 -06:00
parent bd48e2055e
commit a240d312d0
4 changed files with 714 additions and 816 deletions
+1 -3
View File
@@ -20,6 +20,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPArray+KVO.j"
@import "CPArray.j"
@import "CPDictionary.j"
@import "CPException.j"
@@ -1566,6 +1567,3 @@ var _CPKVOInfoMake = function(anObserver, theOptions, aContext, aForwarder)
forwarder: aForwarder
};
};
@import "CPArray+KVO.j"
@import "CPSet+KVO.j"
-440
View File
@@ -1,440 +0,0 @@
/*
* CPSet+KVO.j
* Foundation
*
* Created by Daniel Stolzenberg.
* Copyright 2010, University of Rostock
*
* 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 "CPException.j"
@import "CPObject.j"
@import "CPMutableSet.j"
@import "CPNull.j"
@import "_CPCollectionKVCOperators.j"
@implementation CPObject (CPSetKVO)
- (id)mutableSetValueForKey:(id)aKey
{
return [[_CPKVCSet alloc] initWithKey:aKey forProxyObject:self];
}
- (id)mutableSetValueForKeyPath:(id)aKeyPath
{
var dotIndex = aKeyPath.indexOf(".");
if (dotIndex < 0)
return [self mutableSetValueForKey:aKeyPath];
var firstPart = aKeyPath.substring(0, dotIndex),
lastPart = aKeyPath.substring(dotIndex + 1);
return [[self valueForKeyPath:firstPart] mutableSetValueForKeyPath:lastPart];
}
@end
@implementation _CPKVCSet : CPMutableSet
{
id _proxyObject;
id _key;
SEL _accessSEL;
Function _access;
SEL _setSEL;
Function _set;
SEL _countSEL;
Function _count;
SEL _enumeratorSEL;
Function _enumerator;
SEL _memberSEL;
Function _member;
SEL _addSEL;
Function _add;
SEL _addManySEL;
Function _addMany;
SEL _removeSEL;
Function _remove;
SEL _removeManySEL;
Function _removeMany;
SEL _intersectSEL;
Function _intersect;
}
+ (id)alloc
{
var set = [CPMutableSet set];
set.isa = self;
var ivars = class_copyIvarList(self),
count = ivars.length;
while (count--)
set[ivar_getName(ivars[count])] = nil;
return set;
}
- (id)initWithKey:(id)aKey forProxyObject:(id)anObject
{
self = [super init];
_key = aKey;
_proxyObject = anObject;
var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
_accessSEL = sel_getName(_key);
if ([_proxyObject respondsToSelector:_accessSEL])
_access = [_proxyObject methodForSelector:_accessSEL];
_setSEL = sel_getName(@"set"+capitalizedKey+":");
if ([_proxyObject respondsToSelector:_setSEL])
_set = [_proxyObject methodForSelector:_setSEL];
_countSEL = sel_getName(@"countOf"+capitalizedKey);
if ([_proxyObject respondsToSelector:_countSEL])
_count = [_proxyObject methodForSelector:_countSEL];
_enumeratorSEL = sel_getName(@"enumeratorOf"+capitalizedKey);
if ([_proxyObject respondsToSelector:_enumeratorSEL])
_enumerator = [_proxyObject methodForSelector:_enumeratorSEL];
_memberSEL = sel_getName(@"memberOf"+capitalizedKey+":");
if ([_proxyObject respondsToSelector:_memberSEL])
_member = [_proxyObject methodForSelector:_memberSEL];
_addSEL = sel_getName(@"add"+capitalizedKey+"Object:");
if ([_proxyObject respondsToSelector:_addSEL])
_add = [_proxyObject methodForSelector:_addSEL];
_addManySEL = sel_getName(@"add"+capitalizedKey+":");
if ([_proxyObject respondsToSelector:_addManySEL])
_addMany = [_proxyObject methodForSelector:_addManySEL];
_removeSEL = sel_getName(@"remove"+capitalizedKey+"Object:");
if ([_proxyObject respondsToSelector:_removeSEL])
_remove = [_proxyObject methodForSelector:_removeSEL];
_removeManySEL = sel_getName(@"remove"+capitalizedKey+":");
if ([_proxyObject respondsToSelector:_removeManySEL])
_removeMany = [_proxyObject methodForSelector:_removeManySEL];
_intersectSEL = sel_getName(@"intersect"+capitalizedKey+":");
if ([_proxyObject respondsToSelector:_intersectSEL])
_intersect = [_proxyObject methodForSelector:_intersectSEL];
return self;
}
- (id)_representedObject
{
if (_access)
return _access(_proxyObject, _accessSEL);
return [_proxyObject valueForKey:_key];
}
- (void)_setRepresentedObject:(id)anObject
{
if (_set)
return _set(_proxyObject, _setSEL, anObject);
[_proxyObject setValue:anObject forKey:_key];
}
- (CPUInteger)count
{
if (_count)
return _count(_proxyObject, _countSEL);
return [[self _representedObject] count];
}
- (CPEnumerator)objectEnumerator
{
if (_enumerator)
return _enumerator(_proxyObject, _enumeratorSEL);
return [[self _representedObject] objectEnumerator];
}
- (id)member:(id)anObject
{
if (_member)
return _member(_proxyObject, _memberSEL, anObject);
return [[self _representedObject] member:anObject];
}
- (void)addObject:(id)anObject
{
if (_add)
_add(_proxyObject, _addSEL, anObject);
else if (_addMany)
{
var objectSet = [CPSet setWithObject: anObject];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target addObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)addObjectsFromArray:(CPArray)objects
{
if (_addMany)
{
var objectSet = [CPSet setWithArray: objects];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else if (_add)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
_add(_proxyObject, _addSEL, object);
}
else
{
var target = [[self _representedObject] copy];
[target addObjectsFromArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)unionSet:(CPSet)aSet
{
if (_addMany)
_addMany(_proxyObject, _addManySEL, aSet);
else if (_add)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
_add(_proxyObject, _addSEL, object);
}
else
{
var target = [[self _representedObject] copy];
[target unionSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObject:(id)anObject
{
if (_remove)
_remove(_proxyObject, _removeSEL, anObject);
else if (_removeMany)
{
var objectSet = [CPSet setWithObject: anObject];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target removeObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)minusSet:(CPSet)aSet
{
if (_removeMany)
_removeMany(_proxyObject, _removeManySEL, aSet);
else if (_remove)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
_remove(_proxyObject, _removeSEL, object);
}
else
{
var target = [[self _representedObject] copy];
[target minusSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObjectsInArray:(CPArray)objects
{
if (_removeMany)
{
var objectSet = [CPSet setWithArray:objects];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else if (_remove)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
_remove(_proxyObject, _removeSEL, object);
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsInArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)removeAllObjects
{
if (_removeMany)
{
var allObjectsSet = [[self _representedObject] copy];
_removeMany(_proxyObject, _removeManySEL, allObjectsSet);
}
else if (_remove)
{
var object,
objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
_remove(_proxyObject, _removeSEL, object);
}
else
{
var target = [[self _representedObject] copy];
[target removeAllObjects];
[self _setRepresentedObject:target];
}
}
- (void)intersectSet:(CPSet)aSet
{
if (_intersect)
_intersect(_proxyObject, _intersectSEL, aSet);
else
{
var target = [[self _representedObject] copy];
[target intersectSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)setSet:(CPSet)set
{
[self _setRepresentedObject: set];
}
- (CPArray)allObjects
{
return [[self _representedObject] allObjects];
}
- (id)anyObject
{
return [[self _representedObject] anyObject];
}
- (BOOL)containsObject:(id)anObject
{
return [[self _representedObject] containsObject: anObject];
}
- (BOOL)intersectsSet:(CPSet)aSet
{
return [[self _representedObject] intersectsSet: aSet];
}
- (BOOL)isEqualToSet:(CPSet)aSet
{
return [[self _representedObject] isEqualToSet: aSet];
}
- (id)copy
{
return [[self _representedObject] copy];
}
@end
@implementation CPSet (CPKeyValueCoding)
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (!aKeyPath)
[self valueForUndefinedKey:@"<empty path>"];
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
parameter;
if (dotIndex !== -1)
{
operator = aKeyPath.substring(1, dotIndex);
parameter = aKeyPath.substring(dotIndex + 1);
}
else
operator = aKeyPath.substring(1);
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
var valuesForKeySet = [CPSet set],
containedObject,
containedObjectValue,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
if (containedObjectValue == nil)
containedObjectValue = [CPNull null];
[valuesForKeySet addObject:containedObjectValue];
}
return valuesForKeySet;
}
}
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
var containedObject,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
[containedObject setValue:aValue forKey:aKey];
}
@end
+587 -79
View File
@@ -1,149 +1,657 @@
/*!
@class CPMutableSet
@ingroup compatability
This class is just an empty subclass of CPSet.
CPSet already implements mutable methods and
this class only exists for source compatability.
*/
/*
* CPMutableSet.j
* Foundation
*
* Core mutable subclass interface and Key-Value Observing proxy implementations.
*/
@import "_CPSet.j"
/*!
@class CPMutableSet
@ingroup compatability
This class is just an empty subclass of CPSet.
CPSet already implements mutable methods and
this class only exists for source compatibility.
*/
@implementation CPMutableSet : CPSet
/*!
Returns an initialized set with a given initial capacity.
@param aCapacity, only present for compatability
*/
Returns an initialized set with a given initial capacity.
@param aCapacity, only present for compatibility
*/
- (id)initWithCapacity:(unsigned)aCapacity
{
return [self init];
return [self init];
}
/*!
Creates and returns a set with a given initial capacity.
@param aCapacity, only present for compatability
*/
Creates and returns a set with a given initial capacity.
@param aCapacity, only present for compatibility
*/
+ (id)setWithCapacity:(CPUInteger)aCapacity
{
return [[self alloc] initWithCapacity:aCapacity];
return [[self alloc] initWithCapacity:aCapacity];
}
/*!
Returns a set filtered using a given predicate.
@prarm aPredicate a CPPredicate object used to filter the objects in the set.
*/- (void)filterUsingPredicate:(CPPredicate)aPredicate
Returns a set filtered using a given predicate.
@prarm aPredicate a CPPredicate object used to filter the objects in the set.
*/
- (void)filterUsingPredicate:(CPPredicate)aPredicate
{
var object,
objectEnumerator = [self objectEnumerator];
var object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if (![aPredicate evaluateWithObject:object])
[self removeObject:object];
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aPredicate evaluateWithObject:object])
{
[self removeObject:object];
}
}
}
/*!
Removes an object from the set.
@param anObject the object to remove from the set. If the object is not in the set, the method will not modify the set.
*/
Removes an object from the set.
@param anObject the object to remove from the set. If the object is not in the set, the method will not modify the set.
*/
- (void)removeObject:(id)anObject
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Removes an array of objects from the set.
@param anArray an array of object to remove from the set.
*/
Removes an array of objects from the set.
@param anArray an array of object to remove from the set.
*/
- (void)removeObjectsInArray:(CPArray)anArray
{
var index = 0,
count = [anArray count];
var index = 0,
count = [anArray count];
for (; index < count; ++index)
[self removeObject:[anArray objectAtIndex:index]];
for (; index < count; ++index)
{
[self removeObject:[anArray objectAtIndex:index]];
}
}
/*!
Removes all the objects from the set.
*/
Removes all the objects from the set.
*/
- (void)removeAllObjects
{
var object,
objectEnumerator = [self objectEnumerator];
var object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
[self removeObject:object];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
}
/*!
Adds to the receiver each object contained in a given array that is not already a member.
@param array An array of objects to add to the receiver.
*/
Adds to the receiver each object contained in a given array that is not already a member.
@param array An array of objects to add to the receiver.
*/
- (void)addObjectsFromArray:(CPArray)objects
{
var count = [objects count];
var count = [objects count];
while (count--)
[self addObject:objects[count]];
while (count--)
{
[self addObject:objects[count]];
}
}
/*!
Adds to the receiver each object contained in another given set
@param set The set of objects to add to the receiver.
*/
Adds to the receiver each object contained in another given set
@param set The set of objects to add to the receiver.
*/
- (void)unionSet:(CPSet)aSet
{
var object,
objectEnumerator = [aSet objectEnumerator];
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
[self addObject:object];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self addObject:object];
}
}
/*!
Removes from the receiver each object contained in another given set that is present in the receiver.
@param set The set of objects to remove from the receiver.
*/
Removes from the receiver each object contained in another given set that is present in the receiver.
@param set The set of objects to remove from the receiver.
*/
- (void)minusSet:(CPSet)aSet
{
var object,
objectEnumerator = [aSet objectEnumerator];
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
[self removeObject:object];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
}
/*!
Removes from the receiver each object that isn't a member of another given set.
@param set The set with which to perform the intersection.
*/
Removes from the receiver each object that isn't a member of another given set.
@param set The set with which to perform the intersection.
*/
- (void)intersectSet:(CPSet)aSet
{
var object,
objectEnumerator = [self objectEnumerator],
objectsToRemove = [];
var object,
objectEnumerator = [self objectEnumerator],
objectsToRemove = [];
while ((object = [objectEnumerator nextObject]) != nil)
if (![aSet containsObject:object])
objectsToRemove.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aSet containsObject:object])
{
objectsToRemove.push(object);
}
}
var count = [objectsToRemove count];
var count = [objectsToRemove count];
while (count--)
[self removeObject:objectsToRemove[count]];
while (count--)
{
[self removeObject:objectsToRemove[count]];
}
}
/*!
Empties the receiver, then adds to the receiver each object contained in another given set.
@param set The set whose members replace the receiver's content.
*/
Empties the receiver, then adds to the receiver each object contained in another given set.
@param set The set whose members replace the receiver's content.
*/
- (void)setSet:(CPSet)aSet
{
[self removeAllObjects];
[self unionSet:aSet];
[self removeAllObjects];
[self unionSet:aSet];
}
@end
//@import "_CPConcreteMutableSet.j"
#pragma mark -
#pragma mark Category: CPKeyValueCoding
@implementation CPSet (CPKeyValueCoding)
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (!aKeyPath)
{
[self valueForUndefinedKey:@"<empty path>"];
}
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
parameter;
if (dotIndex !== -1)
{
operator = aKeyPath.substring(1, dotIndex);
parameter = aKeyPath.substring(dotIndex + 1);
}
else
{
operator = aKeyPath.substring(1);
}
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
var valuesForKeySet = [CPSet set],
containedObject,
containedObjectValue,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
if (containedObjectValue == nil)
{
containedObjectValue = [CPNull null];
}
[valuesForKeySet addObject:containedObjectValue];
}
return valuesForKeySet;
}
}
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
var containedObject,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
[containedObject setValue:aValue forKey:aKey];
}
}
@end
#pragma mark -
#pragma mark Key-Value Observing Extensions
@implementation CPObject (CPSetKVO)
- (id)mutableSetValueForKey:(id)aKey
{
return [[_CPKVCSet alloc] initWithKey:aKey forProxyObject:self];
}
- (id)mutableSetValueForKeyPath:(id)aKeyPath
{
var dotIndex = aKeyPath.indexOf(".");
if (dotIndex < 0)
{
return [self mutableSetValueForKey:aKeyPath];
}
var firstPart = aKeyPath.substring(0, dotIndex),
lastPart = aKeyPath.substring(dotIndex + 1);
return [[self valueForKeyPath:firstPart] mutableSetValueForKeyPath:lastPart];
}
@end
#pragma mark -
#pragma mark Key-Value Observing Proxy Implementation
@implementation _CPKVCSet : CPMutableSet
{
id _proxyObject;
id _key;
SEL _accessSEL;
Function _access;
SEL _setSEL;
Function _set;
SEL _countSEL;
Function _count;
SEL _enumeratorSEL;
Function _enumerator;
SEL _memberSEL;
Function _member;
SEL _addSEL;
Function _add;
SEL _addManySEL;
Function _addMany;
SEL _removeSEL;
Function _remove;
SEL _removeManySEL;
Function _removeMany;
SEL _intersectSEL;
Function _intersect;
}
+ (id)alloc
{
var set = [CPMutableSet set];
set.isa = self;
var ivars = class_copyIvarList(self),
count = ivars.length;
while (count--)
{
set[ivar_getName(ivars[count])] = nil;
}
return set;
}
- (id)initWithKey:(id)aKey forProxyObject:(id)anObject
{
self = [super init];
_key = aKey;
_proxyObject = anObject;
var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
_accessSEL = sel_getName(_key);
if ([_proxyObject respondsToSelector:_accessSEL])
{
_access = [_proxyObject methodForSelector:_accessSEL];
}
_setSEL = sel_getName(@"set" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_setSEL])
{
_set = [_proxyObject methodForSelector:_setSEL];
}
_countSEL = sel_getName(@"countOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_countSEL])
{
_count = [_proxyObject methodForSelector:_countSEL];
}
_enumeratorSEL = sel_getName(@"enumeratorOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_enumeratorSEL])
{
_enumerator = [_proxyObject methodForSelector:_enumeratorSEL];
}
_memberSEL = sel_getName(@"memberOf" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_memberSEL])
{
_member = [_proxyObject methodForSelector:_memberSEL];
}
_addSEL = sel_getName(@"add" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_addSEL])
{
_add = [_proxyObject methodForSelector:_addSEL];
}
_addManySEL = sel_getName(@"add" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_addManySEL])
{
_addMany = [_proxyObject methodForSelector:_addManySEL];
}
_removeSEL = sel_getName(@"remove" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_removeSEL])
{
_remove = [_proxyObject methodForSelector:_removeSEL];
}
_removeManySEL = sel_getName(@"remove" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_removeManySEL])
{
_removeMany = [_proxyObject methodForSelector:_removeManySEL];
}
_intersectSEL = sel_getName(@"intersect" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_intersectSEL])
{
_intersect = [_proxyObject methodForSelector:_intersectSEL];
}
return self;
}
- (id)_representedObject
{
if (_access)
{
return _access(_proxyObject, _accessSEL);
}
return [_proxyObject valueForKey:_key];
}
- (void)_setRepresentedObject:(id)anObject
{
if (_set)
{
return _set(_proxyObject, _setSEL, anObject);
}
[_proxyObject setValue:anObject forKey:_key];
}
- (CPUInteger)count
{
if (_count)
{
return _count(_proxyObject, _countSEL);
}
return [[self _representedObject] count];
}
- (CPEnumerator)objectEnumerator
{
if (_enumerator)
{
return _enumerator(_proxyObject, _enumeratorSEL);
}
return [[self _representedObject] objectEnumerator];
}
- (id)member:(id)anObject
{
if (_member)
{
return _member(_proxyObject, _memberSEL, anObject);
}
return [[self _representedObject] member:anObject];
}
- (void)addObject:(id)anObject
{
if (_add)
{
_add(_proxyObject, _addSEL, anObject);
}
else if (_addMany)
{
var objectSet = [CPSet setWithObject:anObject];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target addObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)addObjectsFromArray:(CPArray)objects
{
if (_addMany)
{
var objectSet = [CPSet setWithArray:objects];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else if (_add)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target addObjectsFromArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)unionSet:(CPSet)aSet
{
if (_addMany)
{
_addMany(_proxyObject, _addManySEL, aSet);
}
else if (_add)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target unionSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObject:(id)anObject
{
if (_remove)
{
_remove(_proxyObject, _removeSEL, anObject);
}
else if (_removeMany)
{
var objectSet = [CPSet setWithObject:anObject];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target removeObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)minusSet:(CPSet)aSet
{
if (_removeMany)
{
_removeMany(_proxyObject, _removeManySEL, aSet);
}
else if (_remove)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target minusSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObjectsInArray:(CPArray)objects
{
if (_removeMany)
{
var objectSet = [CPSet setWithArray:objects];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else if (_remove)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsInArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)removeAllObjects
{
if (_removeMany)
{
var allObjectsSet = [[self _representedObject] copy];
_removeMany(_proxyObject, _removeManySEL, allObjectsSet);
}
else if (_remove)
{
var object,
objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeAllObjects];
[self _setRepresentedObject:target];
}
}
- (void)intersectSet:(CPSet)aSet
{
if (_intersect)
{
_intersect(_proxyObject, _intersectSEL, aSet);
}
else
{
var target = [[self _representedObject] copy];
[target intersectSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)setSet:(CPSet)set
{
[self _setRepresentedObject:set];
}
- (CPArray)allObjects
{
return [[self _representedObject] allObjects];
}
- (id)anyObject
{
return [[self _representedObject] anyObject];
}
- (BOOL)containsObject:(id)anObject
{
return [[self _representedObject] containsObject:anObject];
}
- (BOOL)intersectsSet:(CPSet)aSet
{
return [[self _representedObject] intersectsSet:aSet];
}
- (BOOL)isEqualToSet:(CPSet)aSet
{
return [[self _representedObject] isEqualToSet:aSet];
}
- (id)copy
{
return [[self _representedObject] copy];
}
@end
+126 -294
View File
@@ -12,493 +12,329 @@
* 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 "CPArray.j"
@import "CPEnumerator.j"
@import "CPException.j"
@import "CPNull.j"
@import "CPNumber.j"
@import "CPObject.j"
@import "_CPCollectionKVCOperators.j"
@class _CPConcreteMutableSet
/*!
@class CPMutableSet
@ingroup Foundation
CPSet is a data structure for storing an an unordered collection of unique objects.
Sets have O(1) insertion/lookup/deletion time complexity.
*/
@class CPSet
@ingroup Foundation
*/
@implementation CPSet : CPObject
{
}
+ (id)alloc
{
if (self === [CPSet class] || self === [CPMutableSet class])
return [_CPPlaceholderSet alloc];
if (self === [CPSet class] || self === [CPMutableSet class])
return [_CPPlaceholderSet alloc];
return [super alloc];
return [super alloc];
}
/*!
Creates and returns an empty set.
*/
+ (id)set
{
return [[self alloc] init];
return [[self alloc] init];
}
/*!
Creates and returns a set containing a uniqued collection of those objects contained in a given array.
@param anArray array containing the objects to add to the new set. If the same object appears more than once objects, it is added only once to the returned set.
*/
+ (id)setWithArray:(CPArray)anArray
{
return [[self alloc] initWithArray:anArray];
return [[self alloc] initWithArray:anArray];
}
/*!
Creates and returns a set that contains a single given object.
@param anObject The object to add to the new set.
*/
+ (id)setWithObject:(id)anObject
{
return [[self alloc] initWithObjects:anObject];
return [[self alloc] initWithObjects:anObject];
}
/*!
Creates and returns a set containing a specified number of objects from a given array of objects.
@param objects A array of objects to add to the new set. If the same object appears more than once objects, it is added only once to the returned set.
@param count The number of objects from objects to add to the new set.
*/
+ (id)setWithObjects:(id)objects count:(CPUInteger)count
{
return [[self alloc] initWithObjects:objects count:count];
return [[self alloc] initWithObjects:objects count:count];
}
/*!
Creates and returns a set containing the objects in a given argument list.
@param anObject The first object to add to the new set.
@param ... A comma-separated list of objects, ending with nil, to add to the new set. If the same object appears more than once objects, it is added only once to the returned set.
*/
+ (id)setWithObjects:(id)anObject, ...
{
var argumentsArray = Array.prototype.slice.apply(arguments);
var argumentsArray = Array.prototype.slice.apply(arguments);
argumentsArray[0] = [self alloc];
argumentsArray[1] = @selector(initWithObjects:);
argumentsArray[0] = [self alloc];
argumentsArray[1] = @selector(initWithObjects:);
return objj_msgSend.apply(this, argumentsArray);
return objj_msgSend.apply(this, argumentsArray);
}
/*!
Creates and returns a set containing the objects from another set.
@param aSet A set containing the objects to add to the new set.
*/
+ (id)setWithSet:(CPSet)set
{
return [[self alloc] initWithSet:set];
return [[self alloc] initWithSet:set];
}
/*!
Creates and returns a set by adding anObject.
@param anObject to add to the new set.
*/
- (id)setByAddingObject:(id)anObject
{
return [[self class] setWithArray:[[self allObjects] arrayByAddingObject:anObject]];
return [[self class] setWithArray:[[self allObjects] arrayByAddingObject:anObject]];
}
/*!
Creates and returns a set by adding the objects from another set.
@param aSet to add objects to add to the new set.
*/
- (id)setByAddingObjectsFromSet:(CPSet)aSet
{
return [self setByAddingObjectsFromArray:[aSet allObjects]];
return [self setByAddingObjectsFromArray:[aSet allObjects]];
}
/*!
Creates and returns a set by adding the objects from an array.
@param anArray with objects to add to a new set.
*/
- (id)setByAddingObjectsFromArray:(CPArray)anArray
{
return [[self class] setWithArray:[[self allObjects] arrayByAddingObjectsFromArray:anArray]];
return [[self class] setWithArray:[[self allObjects] arrayByAddingObjectsFromArray:anArray]];
}
/*!
Basic initializer, returns an empty set.
*/
- (id)init
{
return [self initWithObjects:nil count:0];
return [self initWithObjects:nil count:0];
}
/*!
Initializes a newly allocated set with the objects that are contained in a given array.
@param array An array of objects to add to the new set. If the same object appears more than once in array, it is represented only once in the returned set.
*/
- (id)initWithArray:(CPArray)anArray
{
return [self initWithObjects:anArray count:[anArray count]];
return [self initWithObjects:anArray count:[anArray count]];
}
/*!
Initializes a newly allocated set with members taken from the specified list of objects.
@param anObject The first object to add to the new set.
@param ... A comma-separated list of objects, ending with nil, to add to the new set. If the same object appears more than once in the list, it is represented only once in the returned set.
*/
- (id)initWithObjects:(id)anObject, ...
{
var index = 2,
count = arguments.length;
var index = 2,
count = arguments.length;
for (; index < count; ++index)
if (arguments[index] === nil)
break;
for (; index < count; ++index)
if (arguments[index] === nil)
break;
return [self initWithObjects:Array.prototype.slice.call(arguments, 2, index) count:index - 2];
return [self initWithObjects:Array.prototype.slice.call(arguments, 2, index) count:index - 2];
}
/*!
Creates and returns a set containing the objects from an array.
@param anArray An array containing the objects to add to the new set.
@param aCount the number of objects in anArray.
*/
- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount
{
if (self === _CPSharedPlaceholderSet)
return [[_CPConcreteMutableSet alloc] initWithObjects:objects count:aCount];
if (self === _CPSharedPlaceholderSet)
return [[_CPConcreteMutableSet alloc] initWithObjects:objects count:aCount];
return [super init];
return [super init];
}
/*!
Initializes a newly allocated set and adds to it objects from another given set.
@param aSet a set containing objects to add to the new set.
*/
- (id)initWithSet:(CPSet)aSet
{
return [self initWithArray:[aSet allObjects]];
return [self initWithArray:[aSet allObjects]];
}
/*!
Initializes a newly allocated set and adds to it members of another given set. Only included for compatability.
@param aSet a set of objects to add to the new set.
@param shouldCopyItems a boolean value. If YES the objects would be copied, if NO the objects will not be copied.
*/
- (id)initWithSet:(CPSet)aSet copyItems:(BOOL)shouldCopyItems
{
if (shouldCopyItems)
return [aSet valueForKey:@"copy"];
if (shouldCopyItems)
return [aSet valueForKey:@"copy"];
return [self initWithSet:aSet];
return [self initWithSet:aSet];
}
/*!
Returns the number of members in the receiver.
*/
- (CPUInteger)count
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns an array containing the receivers members, or an empty array if the receiver has no members.
*/
- (CPArray)allObjects
{
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
objects.push(object);
return objects;
return objects;
}
/*!
Returns one of the objects in the receiver, or nil if the receiver contains no objects.
*/
- (id)anyObject
{
return [[self objectEnumerator] nextObject];
return [[self objectEnumerator] nextObject];
}
/*!
Returns a Boolean value that indicates whether a given object is present in the receiver.
@param anObject The object for which to test membership of the receiver.
*/
- (BOOL)containsObject:(id)anObject
{
return [self member:anObject] != nil;
return [self member:anObject] != nil;
}
/*!
Returns a set filtered using a given predicate.
@prarm aPredicate a CPPredicate object used to filter the objects in the set.
*/
- (CPSet)filteredSetUsingPredicate:(CPPredicate)aPredicate
{
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if ([aPredicate evaluateWithObject:object])
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
if ([aPredicate evaluateWithObject:object])
objects.push(object);
return [[[self class] alloc] initWithArray:objects];
return [[[self class] alloc] initWithArray:objects];
}
/*!
Sends to each object in the receiver a message specified by a given selector.
@param aSelector A selector that specifies the message to send to the members of the receiver. The method must not take any arguments. It should not have the side effect of modifying the receiver. This value must not be NULL.
*/
- (void)makeObjectsPerformSelector:(SEL)aSelector
{
[self makeObjectsPerformSelector:aSelector withObjects:nil];
[self makeObjectsPerformSelector:aSelector withObjects:nil];
}
/*!
Sends to each object in the receiver a message specified by a given selector.
@param aSelector A selector that specifies the message to send to the receiver's members. The method must take a single argument of type id. The method should not, as a side effect, modify the receiver. The value must not be NULL.
@param anObject The object to pass as an argument to the method specified by aSelector.
*/
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject
{
[self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
[self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
}
/*!
Sends to each object in the receiver a message specified by a given selector.
@param aSelector A selector that specifies the message to send to the receiver's members. The method must take a single argument of type id. The method should not, as a side effect, modify the receiver. The value must not be NULL.
@param objects The objects to pass as an argument to the method specified by aSelector.
*/
- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
{
var object,
objectEnumerator = [self objectEnumerator],
argumentsArray = [nil, aSelector].concat(objects || []);
var object,
objectEnumerator = [self objectEnumerator],
argumentsArray = [nil, aSelector].concat(objects || []);
while ((object = [objectEnumerator nextObject]) != nil)
{
argumentsArray[0] = object;
objj_msgSend.apply(this, argumentsArray);
}
while ((object = [objectEnumerator nextObject]) != nil)
{
argumentsArray[0] = object;
objj_msgSend.apply(this, argumentsArray);
}
}
/*!
Determines whether the receiver contains an object equal to a given object, and returns that object if it is present.
@param anObject The object for which to test for membership of the receiver.
*/
- (id)member:(id)anObject
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns an object enumerator (CPEnumerator) for the receiver.
*/
- (CPEnumerator)objectEnumerator
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Enumberates over the objects in a set using a given function.
@param aFunction a callback for each itteration, should be of the format: function(anObject, stop).
*/
- (void)enumerateObjectsUsingBlock:(Function /*(id anObject, @ref BOOL stop)*/)aFunction
- (void)enumerateObjectsUsingBlock:(Function)aFunction
{
var object,
objectEnumerator = [self objectEnumerator],
shouldStop = NO;
var object,
objectEnumerator = [self objectEnumerator],
shouldStop = NO;
while (!shouldStop && (object = [objectEnumerator nextObject]) != nil) {
if (aFunction(object, @ref(shouldStop)) !== undefined) {
throw "DEPRECATED: The method enumerateObjectsUsingBlock: does not support returning a value in the block to stop the iteration. Please use the stop variable";
}
}
while (!shouldStop && (object = [objectEnumerator nextObject]) != nil) {
if (aFunction(object, @ref(shouldStop)) !== undefined) {
throw "DEPRECATED: The method enumerateObjectsUsingBlock: does not support returning a value in the block to stop the iteration.";
}
}
}
// FIXME: stop is broken.
- (CPSet)objectsPassingTest:(Function)aFunction
{
var objects = [],
object = nil,
objectEnumerator = [self objectEnumerator];
var objects = [],
object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if (aFunction(object))
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
if (aFunction(object))
objects.push(object);
return [[[self class] alloc] initWithArray:objects];
return [[[self class] alloc] initWithArray:objects];
}
/*!
Returns a Boolean value that indicates whether every object in the receiver is also present in another given set.
@param set The set with which to compare the receiver.
*/
- (BOOL)isSubsetOfSet:(CPSet)aSet
{
var object = nil,
objectEnumerator = [self objectEnumerator];
var object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if (![aSet containsObject:object])
return NO;
while ((object = [objectEnumerator nextObject]) != nil)
if (![aSet containsObject:object])
return NO;
return YES;
return YES;
}
/*!
Returns a Boolean value that indicates whether at least one object in the receiver is also present in another given set.
@param set The set with which to compare the receiver.
*/
- (BOOL)intersectsSet:(CPSet)aSet
{
if (self === aSet)
// The empty set intersects nothing
return [self count] > 0;
if (self === aSet)
return [self count] > 0;
var object = nil,
objectEnumerator = [self objectEnumerator];
var object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if ([aSet containsObject:object])
return YES;
while ((object = [objectEnumerator nextObject]) != nil)
if ([aSet containsObject:object])
return YES;
return NO;
return NO;
}
/*!
Returns an array of the set's content sorted as specified by a given array of sort descriptors.
@param sortDescriptors an array of CPSortDescriptor objects.
*/
- (CPArray)sortedArrayUsingDescriptors:(CPArray)someSortDescriptors
{
return [[self allObjects] sortedArrayUsingDescriptors:someSortDescriptors];
return [[self allObjects] sortedArrayUsingDescriptors:someSortDescriptors];
}
/*!
Compares the receiver to another set.
@param set The set with which to compare the receiver.
*/
- (BOOL)isEqualToSet:(CPSet)aSet
{
return [self isEqual:aSet];
return [self isEqual:aSet];
}
/*!
Returns YES if BOTH sets are a subset of the other.
@param aSet a set of objects
*/
- (BOOL)isEqual:(CPSet)aSet
{
// If both are subsets of each other, they are equal
return self === aSet ||
[aSet isKindOfClass:[CPSet class]] &&
([self count] === [aSet count] &&
[aSet isSubsetOfSet:self]);
return self === aSet ||
[aSet isKindOfClass:[CPSet class]] &&
([self count] === [aSet count] &&
[aSet isSubsetOfSet:self]);
}
- (CPString)description
{
var string = "{(\n",
objects = [self allObjects],
index = 0,
count = [objects count];
var string = "{(\n",
objects = [self allObjects],
index = 0,
count = [objects count];
for (; index < count; ++index)
{
var object = objects[index];
for (; index < count; ++index)
{
var object = objects[index];
string += "\t" + String(object).split('\n').join("\n\t") + "\n";
}
string += "\t" + String(object).split('\n').join("\n\t") + "\n";
}
return string + ")}";
return string + ")}";
}
@end
#pragma mark -
#pragma mark Category: CPCopying
@implementation CPSet (CPCopying)
- (id)copy
{
return [[self class] setWithSet:self];
return [[self class] setWithSet:self];
}
- (id)mutableCopy
{
return [self copy];
return [self copy];
}
@end
#pragma mark -
#pragma mark Category: CPCoding
var CPSetObjectsKey = @"CPSetObjectsKey";
@implementation CPSet (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
return [self initWithArray:[aCoder decodeObjectForKey:CPSetObjectsKey]];
return [self initWithArray:[aCoder decodeObjectForKey:CPSetObjectsKey]];
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:[self allObjects] forKey:CPSetObjectsKey];
[aCoder encodeObject:[self allObjects] forKey:CPSetObjectsKey];
}
@end
@implementation CPSet (CPKeyValueCoding)
#pragma mark -
#pragma mark Private Allocation Placeholder
- (id)valueForKey:(CPString)aKey
{
if (aKey === "@count")
return [self count];
var valueSet = [CPSet set],
object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
var value = [object valueForKey:aKey];
[valueSet addObject:value];
}
return valueSet;
}
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
var object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
[object setValue:aValue forKey:aKey];
}
@end
var _CPSharedPlaceholderSet = nil;
var _CPSharedPlaceholderSet = nil;
@implementation _CPPlaceholderSet : CPSet
{
@@ -506,14 +342,10 @@ var _CPSharedPlaceholderSet = nil;
+ (id)alloc
{
if (!_CPSharedPlaceholderSet)
_CPSharedPlaceholderSet = [super alloc];
if (!_CPSharedPlaceholderSet)
_CPSharedPlaceholderSet = [super alloc];
return _CPSharedPlaceholderSet;
return _CPSharedPlaceholderSet;
}
@end
// We actually want _CPConcreteMutableSet, but this introduces the possibility of an invalid @import loop.
// This will be correctly solved when we move to true immutable/mutable pairs.
//@import "CPMutableSet.j"