Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -210,10 +210,25 @@ * @param key The key whose value should be returned * @return The value for the given key or `nil` if the key was not found */ - (nullable id)valueForKey: (OFString*)key; +/*! + * @brief Sets a value for a key. + * + * If the key starts with an `@`, the `@` is stripped and + * `[super setValue:forKey:]` is called. + * If the key does not start with an `@`, this is equivalent to + * @ref setObject:forKey:. In this case, if the dictionary is immutable, an + * @ref OFUndefinedKeyException is thrown. + * + * @param key The key to set + * @param value The value to set the key to + */ +- (void)setValue: (nullable id)value + forKey: (OFString*)key; + /*! * @brief Checks whether the dictionary contains an object equal to the * specified object. * * @param object The object which is checked for being in the dictionary Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -27,10 +27,11 @@ #import "OFXMLElement.h" #import "OFDataArray.h" #import "OFInvalidArgumentException.h" #import "OFOutOfRangeException.h" +#import "OFUndefinedKeyException.h" static struct { Class isa; } placeholder; @@ -296,10 +297,33 @@ return [ret autorelease]; } return [self objectForKey: key]; } + +- (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 (![self isKindOfClass: [OFMutableDictionary class]]) + @throw [OFUndefinedKeyException exceptionWithObject: self + key: key + value: value]; + + [(OFMutableDictionary*)self setObject: value + forKey: key]; +} - (size_t)count { OF_UNRECOGNIZED_SELECTOR } Index: src/OFMutableDictionary.h ================================================================== --- src/OFMutableDictionary.h +++ src/OFMutableDictionary.h @@ -75,24 +75,10 @@ - (void)setObject: (ObjectType)object forKey: (KeyType)key; - (void)setObject: (ObjectType)object forKeyedSubscript: (KeyType)key; -/*! - * @brief Sets a value for a key. - * - * If the key starts with an `@`, the `@` is stripped and - * `[super setValue:forKey:]` is called. - * If the key does not start with an `@`, this is equivalent to - * @ref setObject:forKey:. - * - * @param key The key to set - * @param value The value to set the key to - */ -- (void)setValue: (nullable id)value - forKey: (OFString*)key; - /*! * @brief Removes the object for the specified key from the dictionary. * * @param key The key whose object should be removed */ Index: src/OFMutableDictionary.m ================================================================== --- src/OFMutableDictionary.m +++ src/OFMutableDictionary.m @@ -175,28 +175,10 @@ { [self setObject: object forKey: key]; } -- (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; - } - - [self setObject: value - forKey: key]; -} - - (void)removeObjectForKey: (id)key { OF_UNRECOGNIZED_SELECTOR } Index: tests/OFDictionaryTests.m ================================================================== --- tests/OFDictionaryTests.m +++ tests/OFDictionaryTests.m @@ -21,10 +21,11 @@ #import "OFArray.h" #import "OFNumber.h" #import "OFAutoreleasePool.h" #import "OFEnumerationMutationException.h" +#import "OFUndefinedKeyException.h" #import "TestsAppDelegate.h" static OFString *module = @"OFDictionary"; static OFString *keys[] = { @@ -57,10 +58,14 @@ TEST(@"-[valueForKey:]", [[dict valueForKey: keys[0]] isEqual: values[0]] && [[dict valueForKey: @"@count"] isEqual: [OFNumber numberWithSize: 2]]) + + EXPECT_EXCEPTION(@"Catching -[setValue:forKey:] on immutable " + @"dictionary", OFUndefinedKeyException, [dict setValue: @"x" + forKey: @"x"]) TEST(@"-[containsObject:]", [dict containsObject: values[0]] && ![dict containsObject: @"nonexistant"])