Source Code
cs_IntDictionary.h
  Prev   Next
/*
 * 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