// // DSDictionaryCategories.m // Shared categories // // Created by David Sinclair on Sat Aug 10 2002. // Copyright © 2002 - 2007 Dejal Systems, LLC. All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // Redistributions of source code must retain this list of conditions and the following disclaimer. // // The name of Dejal Systems, LLC may not be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THIS SOFTWARE. // #import "DSDictionaryCategories.h" #import "DSStringCategories.h" @implementation NSDictionary (DSDictionaryCategories) /* dictionaryWithArrayOfDictionaries:usingKey: Given an array that contains dictionaries, and a key that is used in those dictionaries, returns a new dictionary that has the values of those keys as the keys, and the dictionaries as the values. Returns nil if the array or key parameters are nil. Effectively the reverse of the -allValues method. Written by DJS 2004-06. */ + (id)dictionaryWithArrayOfDictionaries:(NSArray *)array usingKey:(NSString *)key { if (!array || !key) return nil; NSMutableDictionary *masterDict = [NSMutableDictionary dictionaryWithCapacity:[array count]]; NSEnumerator *enumerator = [array objectEnumerator]; NSDictionary *subDict; while ((subDict = [enumerator nextObject])) { NSString *identifier = [subDict objectForKey:key]; if (identifier) [masterDict setObject:subDict forKey:identifier]; } return masterDict; } /* deepCopy Similar to -copy, but each of the objects in the dictionary are copied too (using the same keys). Note that like -copy, the dictionary is retained. Written by DJS 2004-06. Changed by DJS 2006-01 to fix memory leak through excessive retaining. */ - (id)deepCopy { id dict = [[NSMutableDictionary alloc] init]; NSEnumerator *enumerator = [self keyEnumerator]; id key; id object; id copy; while ((key = [enumerator nextObject])) { object = [self objectForKey:key]; if ([object respondsToSelector:@selector(deepCopy)]) copy = [object deepCopy]; else copy = [object copy]; [dict setObject:copy forKey:key]; // Both -deepCopy and -copy retain the object, and so does -setObject:forKey:, so need to -release: [copy release]; } return dict; } /* emptyStringOrObjectForKey: Returns an entry's value given its key, or an empty string (@"") if no value is associated with aKey. Written by DJS 2004-01. */ - (id)emptyStringOrObjectForKey:(id)aKey { return [self objectForKey:aKey defaultValue:@""]; } /* hasKey: Returns YES if the key exists in the dictionary, or NO if it doesn't. Dubious benefit, since -objectForKey:'s result can be treated as a boolean anyway, but sometimes you want an actual BOOL value. Written by DJS 2005-02. */ - (BOOL)hasKey:(id)key { return ([self objectForKey:key] != nil); } /* sortedKeys Like -allKeys, but sorted in ascending alphabetical order by the dictionary's keys (not case sensitive). Written by DJS 2006-09. */ - (NSArray *)sortedKeys; { return [[self allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; } // NOTE: may want to add -arrayForKey:, -mutableArrayForKey:, and other variations too sometime. /* boolForKey: Returns YES if the value for this key is a string containing "yes" (case insensitive), or a number with a non-zero value. Otherwise returns NO. Written by DJS 2005-02. */ - (BOOL)boolForKey:(id)key { NSString *value = [self stringForKey:key]; if (value) return ([value intValue] || [[value lowercaseString] isEqualToString:@"yes"]); else return NO; } /* integerForKey: Invokes stringForKey: with the key. Returns 0 if no string is returned. Otherwise, the resulting string is sent an intValue message, which provides this method's return value. Written by DJS 2005-02. */ - (int)integerForKey:(id)key { NSString *value = [self stringForKey:key]; if (value) return [value intValue]; else return 0; } /* floatForKey: Invokes stringForKey: with the key. Returns 0.0 if no string is returned. Otherwise, the resulting string is sent a floatValue message, which provides this method's return value. Written by DJS 2005-02. */ - (float)floatForKey:(id)key { NSString *value = [self stringForKey:key]; if (value) return [value floatValue]; else return 0.0; } /* stringForKey: Like -objectForKey:, but always returns a string, or nil if there is no object with that key. Uses -description to convert any non-string types to a string equivalent. Written by DJS 2005-02. */ - (NSString *)stringForKey:(id)key { return [[self objectForKey:key] description]; } /* stringLengthForKey: Returns the length of the object interpreted as a string. See also -containsSomethingForKey:, below. Written by DJS 2005-02. */ - (int)stringLengthForKey:(id)key { return [[self stringForKey:key] length]; } /* containsSomethingForKey: Returns YES if the object interpreted as a string is non-empty, i.e. not nil and not @"". See also -stringLengthForKey:, above. Written by DJS 2005-02. */ - (BOOL)containsSomethingForKey:(id)key { return ([[self stringForKey:key] length] > 0); } /* objectForKey:orBool: Returns an entry's value given its key, or a NSNumber with the aDefault boolean if no value is associated with aKey. Written by DJS 2004-06. */ - (id)objectForKey:(id)aKey orBool:(BOOL)aDefault { id value = [self objectForKey:aKey]; if (!value) value = [NSNumber numberWithBool:aDefault]; return value; } /* objectForKey:orInt: Returns an entry's value given its key, or a NSNumber with the aDefault integer if no value is associated with aKey. Written by DJS 2004-06. */ - (id)objectForKey:(id)aKey orInt:(BOOL)aDefault { id value = [self objectForKey:aKey]; if (!value) value = [NSNumber numberWithInt:aDefault]; return value; } /* objectForKey:defaultValue: Returns an entry's value given its key, or the default value if no value is associated with aKey. Written by DJS 2004-01. */ - (id)objectForKey:(id)aKey defaultValue:(id)aDefault { id value = [self objectForKey:aKey]; if (!value) value = aDefault; return value; } /* dictionaryWithColor: Given a color, returns it encoded as a dictionary, so it can be added to the defaults, etc. Written by DJS 2004-05. */ + (id)dictionaryWithColor:(NSColor *)color { float red, green, blue, alpha; [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha]; return [self dictionaryWithObjectsAndKeys: [NSNumber numberWithFloat:red], @"Red", [NSNumber numberWithFloat:green], @"Green", [NSNumber numberWithFloat:blue], @"Blue", [NSNumber numberWithFloat:alpha], @"Alpha", nil]; } /* color Returns a NSColor from the encoded value of the receiver, which must have been encoded by +dictionaryWithColor: above, or manually constructed (e.g. in a plist file) to have the keys "Red", "Green", "Blue" and "Alpha" -- any of which may be omitted if desired. Written by DJS 2004-05. */ - (NSColor *)color { float red = [[self objectForKey:@"Red"] floatValue]; float green = [[self objectForKey:@"Green"] floatValue]; float blue = [[self objectForKey:@"Blue"] floatValue]; float alpha = 1.0; id temp = [self objectForKey:@"Alpha"]; if (temp) alpha = [temp floatValue]; return [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha]; } /* colorForKey: Returns a color from the defaults with the specified key. Written by DJS 2003-07 for NSUserDefaults category. Changed by DJS 2007-04 to copy to NSDictionary category. */ - (NSColor *)colorForKey:(id)key; { NSString *value = [self stringForKey:key]; if (value) return [value color]; else return nil; } @end // ---------------------------------------------------------------------------------------- #pragma mark - // ---------------------------------------------------------------------------------------- @implementation NSMutableDictionary (DSDictionaryCategories) /* setBool:forKey: Sets a NSNumber value in the mutable dictionary with the specified key. Written by DJS 2005-02. */ - (void)setBool:(BOOL)value forKey:(id)key { [self setObject:[NSNumber numberWithBool:value] forKey:key]; } /* setInteger:forKey: Sets a NSNumber value in the mutable dictionary with the specified key. Written by DJS 2005-02. */ - (void)setInteger:(int)value forKey:(id)key { [self setObject:[NSNumber numberWithInt:value] forKey:key]; } /* setFloat:forKey: Sets a NSNumber value in the mutable dictionary with the specified key. Written by DJS 2005-02. */ - (void)setFloat:(float)value forKey:(id)key { [self setObject:[NSNumber numberWithFloat:value] forKey:key]; } /* setColor:forKey: Stores the specified color in the defaults with the specified key. Written by DJS 2003-07 for NSUserDefaults category. Changed by DJS 2007-04 to copy to NSDictionary category. */ - (void)setColor:(NSColor *)color forKey:(id)key; { [self setObject:[NSString stringWithColor:color] forKey:key]; } /* setObject:forKey:defaultValue: Adds an entry to the receiver, consisting of aKey and its corresponding value object anObject. If anObject is nil, sets the default value instead; does nothing if both objects are nil. Written by DJS 2004-01. */ - (void)setObject:(id)anObject forKey:(id)aKey defaultValue:(id)aDefault { if (anObject) [self setObject:anObject forKey:aKey]; else if (aDefault) [self setObject:aDefault forKey:aKey]; } /* setObject:forKey:removeIfNil: Adds an entry to the receiver, consisting of aKey and its corresponding value object anObject. If anObject is nil and removeIfNil is YES, the key is instead removed from the receiver, if already present, otherwise nothing happens; useful to avoid an exception when an object may be nil. Written by DJS 2004-01. */ - (void)setObject:(id)anObject forKey:(id)aKey removeIfNil:(BOOL)removeIfNil { if (anObject) [self setObject:anObject forKey:aKey]; else if (removeIfNil) [self removeObjectForKey:aKey]; } @end