/*
* cs_IntDictionary.h
*
* Integer-keyed dictionary
*
* Similar to NSDictionary, cs_IntDictionary implements most of NSDictionary's methods
* but using an integer key instead of a generic object [id] key.
*
* It uses a CFMutableDictionaryRef for internal data management, thus automatically
* taking advantages of improvements in Core Foundation.
*
*
* NOTE It implements: -objectForIntKey: -setObject: forIntKey:
* instead of: -objectForKey: -setObject: forKey:
*
* to emphasize the <int> and to prevent inadvertent use of an object as the key.
*
* Other methods are the same as NSDictionary's (allObjects, removeAllObjects, etc).
*
*
* Usage: cs_IntDictionary *d = [cs_IntDictionary dictionary];
*
* [d setObject: @"One" forIntKey: 1];
*
* id a = [d objectForIntKey: 3];
*
* NSEnumerator *enumr = [d objectEnumerator];
* while( (a = [enumr nextObject]) )
* NSLog(@"%@ stored with %d\n", a, [d intKeyForObject: a] );
*
*
*
* Copyright (c) 2003 - 2005 Classical Software. All rights reserved.
*
* This software is released under the Creative Commons Attribution License, version 2.0
* http://creativecommons.org/licenses/by/2.0/legalcode
*/
#import <Cocoa/Cocoa.h>
/*
* **************************************
* *
* cs_IntDictionary *
* *
* **************************************
*/
@interface cs_IntDictionary: NSObject
{
CFMutableDictionaryRef cf_mutable_dict_ref_;
BOOL should_retain_objects_;
}
+ (cs_IntDictionary *) dictionary;
+ (cs_IntDictionary *) dictionaryWithDictionary: (cs_IntDictionary *) d;
- (void) addObjectsFromDictionary: (cs_IntDictionary *) d;
- (void) XOrObjectsFromDictionary: (cs_IntDictionary *) d;
- (id) objectForIntKey: (int) k;
- (void) setObject: (id) o forIntKey: (int) k;
- (void) removeObjectForIntKey: (int) k;
- (void) removeAllObjects;
- (CFIndex) count;
- (BOOL) shouldRetainObjects;
- (void) setShouldRetainObjects: (BOOL) b;
- (int) intKeyForObject: (id) o;
- (id) makeObjectsPerformSelector: (SEL) s;
- (id) makeObjectsPerformSelector: (SEL) s withObject: (id) o;
- (NSArray *) allIntKeys;
- (NSMutableArray *) allObjects;
- (NSMutableArray *) allObjectsSortedByIntKey;
- (int) largestIntKey;
- (NSEnumerator *) objectEnumerator;
@end
cs_IntDictionary.m
/*
* cs_IntDictionary.m
*
* Integer-keyed dictionary. See the header file for an intro/summary.
*
*
* Possible additions:
*
* - (NSIndexSet) allIntKeysIndexSet
*
*
* Copyright (c) 2003 - 2005 Classical Software. All rights reserved.
*
* This software is released under the Creative Commons Attribution License, version 2.0
* http://creativecommons.org/licenses/by/2.0/legalcode
*/
#import "cs_IntDictionary.h"
/*
* cs_IntDictionaryEnumerator
*
* Private subclass of NSEnumerator implementing access
* to the dictionary.
*
* Callers only see a regular NSEnumerator.
*/
@interface cs_IntDictionaryEnumerator: NSEnumerator
{
cs_IntDictionary *dict_;
NSMutableArray *int_keys_;
int kth_;
int n_keys_;
}
- (id) initWithIntDictionary: (cs_IntDictionary *) d;
- (void) setupUsingIntDictionary: (cs_IntDictionary *) d;
@end
static int cs_CompareIntKeys ( id, id, void * );
#pragma mark -
/*
* **********************************************************************************************
* *
* cs_IntDictionary *
* *
* **********************************************************************************************
*/
@implementation cs_IntDictionary
static void xmcItrDictFcn_Description ( const void *, const void *, void * );
static void xmcItrDictFcn_FindIntKeyForObj ( const void *, const void *, void * );
static void xmcItrDictFcn_Perform ( const void *, const void *, void * );
static void xmcItrDictFcn_GatherAllIntKeys ( const void *, const void *, void * );
static void xmcItrDictFcn_MinMaxOfAllIntKeys( const void *, const void *, void * );
static void xmcItrDictFcn_GatherAllObjects ( const void *, const void *, void * );
static void xmcItrDictFcn_AddObjToDict ( const void *, const void *, void * );
static void xmcItrDictFcn_XOrWithDict ( const void *, const void *, void * );
static void xmcItrDictFcn_ReleaseObj ( const void *, const void *, void * );
/*
* dictionary
*/
+ (cs_IntDictionary *) dictionary
{
cs_IntDictionary *new_d = [[cs_IntDictionary alloc] init];
return [new_d autorelease];
}
/*
* dictionaryWithDictionary:
*/
+ (cs_IntDictionary *) dictionaryWithDictionary: (cs_IntDictionary *) d
{
cs_IntDictionary *new_d = [[cs_IntDictionary alloc] init];
[new_d addObjectsFromDictionary: d];
return [new_d autorelease];
}
/*
* init
*/
- (id) init
{
if( (self = [super init]) )
{
cf_mutable_dict_ref_ = CFDictionaryCreateMutable( NULL, 0, NULL, NULL );
[self setShouldRetainObjects: YES];
}
return self;
}
/*
* dealloc
*/
- (void) dealloc
{
if( cf_mutable_dict_ref_ )
CFRelease( cf_mutable_dict_ref_ );
[super dealloc];
}
#pragma mark -
/*
* *********************************************
*/
/*
* count
*/
- (CFIndex) count
{
CFIndex n = cf_mutable_dict_ref_ ? CFDictionaryGetCount( cf_mutable_dict_ref_ ) : 0;
return n;
}
#pragma mark -
/*
* *********************************************
*/
/*
* shouldRetainObjects
*
* If the user retains objects themselves and doesn't want this
* dictionary to retain them it can specify that.
*
* The default is to retain objects added to the dictionary.
*/
- (BOOL) shouldRetainObjects
{
return should_retain_objects_;
}
/*
* setShouldRetainObjects:
*/
- (void) setShouldRetainObjects: (BOOL) b
{
should_retain_objects_ = b;
}
#pragma mark -
/*
* *********************************************
*/
/*
* addObjectsFromDictionary:
*/
- (void) addObjectsFromDictionary: (cs_IntDictionary *) add_to_dict
{
if( cf_mutable_dict_ref_
&& add_to_dict->cf_mutable_dict_ref_ )
{
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_AddObjToDict, add_to_dict );
}
}
/*
* xmcItrDictFcn_AddObjToDict()
*/
static void
xmcItrDictFcn_AddObjToDict(
const void *key,
const void *value,
void *context )
{
cs_IntDictionary *add_to_dict = context;
int ik = (int) key;
id obj = (id ) value;
[add_to_dict setObject: obj forIntKey: ik];
}
/*
* XOrObjectsFromDictionary:
*/
- (void) XOrObjectsFromDictionary: (cs_IntDictionary *) b
{
if( cf_mutable_dict_ref_
&& b->cf_mutable_dict_ref_ )
{
CFDictionaryApplyFunction( b->cf_mutable_dict_ref_, xmcItrDictFcn_XOrWithDict, self );
}
}
/*
* xmcItrDictFcn_XOrWithDict()
*/
static void
xmcItrDictFcn_XOrWithDict(
const void *key,
const void *value,
void *context )
{
cs_IntDictionary *self_dict = context;
int ik = (int) key;
id obj = (id ) value;
if( [self_dict objectForIntKey: ik] ) // is in both self & other_dict ?
[self_dict removeObjectForIntKey: ik]; // remove it
else
[self_dict setObject: obj forIntKey: ik]; // only in other - add it to self
}
#pragma mark -
/*
* *********************************************
*/
/*
* objectForIntKey:
*/
- (id) objectForIntKey: (int) key
{
id obj = nil;
if( cf_mutable_dict_ref_ )
obj = (void *) CFDictionaryGetValue( cf_mutable_dict_ref_, (void *) key );
return obj;
}
/*
* setObject: forIntKey:
*/
- (void) setObject: (id ) obj
forIntKey: (int) key
{
if( cf_mutable_dict_ref_ )
{
CFDictionaryAddValue( cf_mutable_dict_ref_, (void *) key, (void *) obj );
if( should_retain_objects_ )
[obj retain];
}
}
/*
* removeObjectForIntKey:
*/
- (void) removeObjectForIntKey: (int) key
{
if( cf_mutable_dict_ref_ )
{
id obj = nil;
if( should_retain_objects_ )
obj = [self objectForIntKey: key];
CFDictionaryRemoveValue( cf_mutable_dict_ref_, (void *) key );
if( should_retain_objects_ && obj )
[obj release];
}
}
/*
* removeAllObjects
*/
- (void) removeAllObjects
{
if( cf_mutable_dict_ref_ )
{
if( should_retain_objects_ )
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_ReleaseObj, self );
CFDictionaryRemoveAllValues( cf_mutable_dict_ref_ );
}
}
/*
* xmcItrDictFcn_ReleaseObj()
*/
static void
xmcItrDictFcn_ReleaseObj(
const void *key,
const void *value,
void *context )
{
// cs_IntDictionary *dict = context; -- (not needed; here for documentation purposes)
// int ik = (int) key; -- (not needed; here for documentation purposes)
id obj = (id ) value;
[obj release];
}
#pragma mark -
/*
* *********************************************
*/
/*
* x_FindObjArgs
*
* Private struct used to pass args into/out of several fcns
*/
typedef struct x_FindObjArgs
{
id looking_for_obj;
int found_int_key;
}
x_FindObjArgs;
/*
* intKeyForObject:
*/
- (int) intKeyForObject: (id) o
{
int n = [self count];
x_FindObjArgs a;
a.looking_for_obj = o;
a.found_int_key = 0;
if( cf_mutable_dict_ref_ && n > 0 )
{
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_FindIntKeyForObj, &a );
}
return a.found_int_key;
}
/*
* xmcItrDictFcn_FindIntKeyForObj()
*/
static void
xmcItrDictFcn_FindIntKeyForObj(
const void *key,
const void *value,
void *context )
{
x_FindObjArgs *args_p = (x_FindObjArgs *)context;
if( value == args_p->looking_for_obj )
args_p->found_int_key = (int)key;
}
#pragma mark -
/*
* *******************************************
*/
/*
* x_PerformArgs Private struct used to pass args into fcns
*/
typedef struct x_PerformArgs
{
cs_IntDictionary *dict;
SEL selector;
id object;
BOOL sel_has_arg;
}
x_PerformArgs;
/*
* makeObjectsPerformSelector:
*/
- (id) makeObjectsPerformSelector: (SEL) sel
{
if( cf_mutable_dict_ref_ )
{
x_PerformArgs args;
memset( &args, 0, sizeof( args ) );
args.dict = self;
args.selector = sel;
// args.sel_has_arg = NO;
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_Perform, &args );
}
return self;
}
/*
* makeObjectsPerformSelector: withObject:
*/
- (id) makeObjectsPerformSelector: (SEL) sel
withObject: (id ) obj
{
if( cf_mutable_dict_ref_ )
{
x_PerformArgs args;
memset( &args, 0, sizeof( args ) );
args.dict = self;
args.selector = sel;
args.object = obj;
args.sel_has_arg = YES;
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_Perform, &args );
}
return self;
}
/*
* xmcItrDictFcn_Perform()
*/
static void
xmcItrDictFcn_Perform(
const void *key,
const void *value,
void *context )
{
x_PerformArgs *args_p = context;
// int ik = (int) key; // (not needed; here for documentation purposes)
id obj = (id ) value;
if( args_p->sel_has_arg )
[obj performSelector: args_p->selector withObject: args_p->object];
else
[obj performSelector: args_p->selector];
}
#pragma mark -
/*
* *******************************************
*/
/*
* allIntKeys
*/
- (NSArray *) allIntKeys
{
int n = [self count];
NSMutableArray *da = nil;
if( cf_mutable_dict_ref_ && n > 0 )
{
da = [NSMutableArray arrayWithCapacity: n];
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_GatherAllIntKeys, da );
}
return da;
}
/*
* xmcItrDictFcn_GatherAllIntKeys()
*/
static void
xmcItrDictFcn_GatherAllIntKeys(
const void *key,
const void *value,
void *context )
{
NSMutableArray *a = context;
[a addObject: [NSNumber numberWithInt: (int)key]];
}
/*
* allObjects
*/
- (NSMutableArray *) allObjects
{
int n = [self count];
NSMutableArray *a = nil;
if( cf_mutable_dict_ref_ && n > 0 )
{
a = [NSMutableArray arrayWithCapacity: n];
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_GatherAllObjects, a );
}
return a;
}
/*
* xmcItrDictFcn_GatherAllObjects()
*/
static void
xmcItrDictFcn_GatherAllObjects(
const void *key,
const void *value,
void *context )
{
NSMutableArray *a = context;
id o = (id) value;
[a addObject: o];
}
/*
* allObjectsSortedByIntKey
*
* Return all objects, sort by their intKey.
*
* This is a bit of a compound method (meaning, it could
* be implemented as 2 smaller methods) but it seems a bit
* more useful to do it as a single method here.
*
* Might have to re-think this in the future.
*
* Or it might be better to provide this functionality in
* a slightly different form:
*
* - (NSArray *) sortedObjectsUsingFunction:
* - (NSArray *) sortedObjectsUsingMethod:
*/
- (NSMutableArray *) allObjectsSortedByIntKey
{
NSMutableArray *a = [NSMutableArray array];
NSArray *keys = [self allIntKeys];
keys = [keys sortedArrayUsingFunction: cs_CompareIntKeys context: self];
NSEnumerator *enumr = [keys objectEnumerator];
NSNumber *num;
id obj;
while( (num = [enumr nextObject]) )
{
int k = [num intValue];
if( (obj = [self objectForIntKey: k]) )
[a addObject: obj];
}
return a;
}
/*
* cs_CompareIntKeys()
*/
static int
cs_CompareIntKeys(
id a,
id b,
void *ctx )
{
int i_a = [a intValue];
int i_b = [b intValue];
return i_a - i_b;
}
/*
* objectEnumerator
*/
- (NSEnumerator *) objectEnumerator
{
return [[cs_IntDictionaryEnumerator alloc] initWithIntDictionary: self];
}
/*
* largestIntKey
*/
#define NOT_SET_KEY 99997
- (int) largestIntKey
{
int n = [self count];
int minmax[ 2 ] = { NOT_SET_KEY, 0 };
if( cf_mutable_dict_ref_ && n > 0 )
{
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_MinMaxOfAllIntKeys, minmax );
}
return minmax[ 1 ];
}
/*
* xmcItrDictFcn_MinMaxOfAllIntKeys()
*/
static void
xmcItrDictFcn_MinMaxOfAllIntKeys(
const void *key,
const void *value,
void *context )
{
int *minmax = context;
int i = (int) key;
minmax[ 0 ] = minmax[ 0 ] == NOT_SET_KEY ? i : MIN( minmax[ 0 ], i );
minmax[ 1 ] = MAX( minmax[ 1 ], i );
}
#pragma mark -
/*
* *******************************************
*/
/*
* description
*/
- (NSString *) description
{
NSMutableString *s = [NSMutableString stringWithFormat: @"%@ %p: %d entries",
[self class],
self,
[self count]
];
if( cf_mutable_dict_ref_ )
{
CFDictionaryApplyFunction( cf_mutable_dict_ref_, xmcItrDictFcn_Description, s );
}
return s;
}
/*
* xmcItrDictFcn_Description()
*/
static void
xmcItrDictFcn_Description(
const void *key,
const void *value,
void *context )
{
NSMutableString *s = context;
int ik = (int) key;
id obj = (id ) value;
char buf[ 30 ];
sprintf( buf, "%8d (%08x)", ik, ik );
[s appendFormat: @"\n %s = %@", buf, obj];
}
@end
#pragma mark -
/*
* **********************************************************************************************
* *
* cs_IntDictionaryEnumerator *
* *
* **********************************************************************************************
*/
@implementation cs_IntDictionaryEnumerator
/*
* initWithIntDictionary:
*/
- (id) initWithIntDictionary: (cs_IntDictionary *) d
{
if( (self = [self init]) )
{
[self setupUsingIntDictionary: d];
}
return self;
}
/*
* dealloc
*/
- (void) dealloc
{
[dict_ release];
[int_keys_ release];
[super dealloc];
}
/*
* setupUsingIntDictionary:
*/
- (void) setupUsingIntDictionary: (cs_IntDictionary *) d
{
// if( mdxAssert1( [d isKindOfClass: [cs_IntDictionary class]], @"d = %@ (not cs_IntDictionary)",
// [d class] ) )
{
dict_ = [d retain];
int_keys_ = [[d allIntKeys] retain];
kth_ = 0;
n_keys_ = [int_keys_ count];
}
}
/*
* nextObject
*/
- (id) nextObject
{
id val = nil;
if( kth_ < n_keys_ )
{
NSNumber *num = [int_keys_ objectAtIndex: kth_++];
int ind = [num intValue];
val = [dict_ objectForIntKey: ind];
}
return val;
}
@end