Index: src/OFSet.h ================================================================== --- src/OFSet.h +++ src/OFSet.h @@ -219,10 +219,41 @@ * @param object The object which is checked for being in the set * @return A boolean whether the set contains the specified object */ - (bool)containsObject: (nullable ObjectType)object; +/*! + * @brief Returns the value for the specified key + * + * If the key starts with an `@`, the `@` is stripped and + * `[super valueForKey:]` is called. + * If the key does not start with an `@`, a new set with the value for the + * specified key for each object is returned. + * + * @note Unlike with @ref OFArray, any nil values are removed! + * + * @param key The key of the value to return + * @return The value for the specified key + */ +- (nullable id)valueForKey: (OFString*)key; + +/*! + * @brief Set the value for the specified key + * + * If the key starts with an `@`, the `@` is stripped and + * `[super setValue:forKey:]` is called. + * If the key does not start with an `@`, @ref setValue:forKey: is called for + * each object. + * + * @note A @ref OFNull value is translated to nil! + * + * @param value The value for the specified key + * @param key The key of the value to set + */ +- (void)setValue: (nullable id)value + forKey: (OFString*)key; + /*! * @brief Returns an OFEnumerator to enumerate through all objects of the set. * * @returns An OFEnumerator to enumerate through all objects of the set */ Index: src/OFSet.m ================================================================== --- src/OFSet.m +++ src/OFSet.m @@ -21,10 +21,11 @@ #import "OFSet.h" #import "OFSet_hashtable.h" #import "OFString.h" #import "OFArray.h" #import "OFXMLElement.h" +#import "OFNull.h" static struct { Class isa; } placeholder; @@ -210,10 +211,62 @@ - (size_t)count { OF_UNRECOGNIZED_SELECTOR } + +- (id)valueForKey: (OFString*)key +{ + OFMutableSet *ret; + + if ([key hasPrefix: @"@"]) { + void *pool = objc_autoreleasePoolPush(); + id ret; + + key = [key substringWithRange: of_range(1, [key length] - 1)]; + ret = [[super valueForKey: key] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + + ret = [OFMutableSet setWithCapacity: [self count]]; + + for (id object in self) { + id value = [object valueForKey: key]; + + if (value != nil) + [ret addObject: value]; + } + + [ret makeImmutable]; + + return ret; +} + +- (void)setValue: (id)value + forKey: (OFString*)key +{ + if ([key hasPrefix: @"@"]) { + void *pool = objc_autoreleasePoolPush(); + + key = [key substringWithRange: of_range(1, [key length] - 1)]; + [super setValue: value + forKey: key]; + + objc_autoreleasePoolPop(pool); + return; + } + + if (value == [OFNull null]) + value = nil; + + for (id object in self) + [object setValue: value + forKey: key]; +} - (bool)containsObject: (id)object { OF_UNRECOGNIZED_SELECTOR } Index: tests/OFSetTests.m ================================================================== --- tests/OFSetTests.m +++ tests/OFSetTests.m @@ -16,10 +16,11 @@ #include "config.h" #import "OFSet.h" #import "OFArray.h" +#import "OFNumber.h" #import "OFAutoreleasePool.h" #import "OFEnumerationMutationException.h" #import "TestsAppDelegate.h" @@ -128,8 +129,16 @@ ok = true; } TEST(@"Detection of mutation during Fast Enumeration", ok); + TEST(@"-[valueForKey:]", + [(set1 = [[OFSet setWithObjects: @"a", @"ab", @"abc", @"b", nil] + valueForKey: @"length"]) isEqual: [OFSet setWithObjects: + [OFNumber numberWithSize: 1], [OFNumber numberWithSize: 2], + [OFNumber numberWithSize: 3], nil]] && + [[set1 valueForKey: @"@count"] isEqual: + [OFNumber numberWithSize: 3]]) + [pool drain]; } @end