1//
2//  AMutableDictionary.m
3//  ST4
4//
5//  Created by Alan Condit on 4/18/11.
6//  Copyright 2011 Alan Condit. All rights reserved.
7//
8
9#import <Cocoa/Cocoa.h>
10#import "AMutableDictionary.h"
11#import "ACBTree.h"
12
13@implementation AMutableDictionary
14
15@synthesize root;
16@synthesize nodes_av;
17@synthesize nodes_inuse;
18@synthesize nxt_nodeid;
19//@synthesize count;
20@synthesize data;
21@synthesize ptrBuffer;
22
23+ (AMutableDictionary *) newDictionary
24{
25    return [[AMutableDictionary alloc] init];
26}
27
28/** dictionaryWithCapacity
29 *  capacity is meaningless to ACBTree because
30 *  capacity is automatically increased
31 */
32+ (AMutableDictionary *) dictionaryWithCapacity
33{
34    return [[AMutableDictionary alloc] init];
35}
36
37- (id)init
38{
39    self = [super init];
40    if (self) {
41        // Initialization code here.
42        nxt_nodeid = 0;
43        count = 0;
44        root = [ACBTree newNodeWithDictionary:self];
45        root.nodeType = LEAF;
46        root.numrecs = 0;
47        root.updtd = NO;
48        root.lnodeid = 1;
49        root.lnode = nil;
50        root.rnodeid = 0xffff;
51        root.rnode = nil;
52    }
53    return self;
54}
55
56/** initWithCapacity
57 *  capacity is meaningless to ACBTree because
58 *  capacity is automatically increased
59 */
60- (id) initWithCapacity:(NSUInteger)numItems
61{
62    self = [super init];
63    if (self) {
64        // Initialization code here.
65        nxt_nodeid = 0;
66        count = 0;
67        root = [ACBTree newNodeWithDictionary:self];
68        root.nodeType = LEAF;
69        root.numrecs = 0;
70        root.updtd = NO;
71        root.lnodeid = 1;
72        root.lnode = nil;
73        root.rnodeid = 0xffff;
74        root.rnode = nil;
75    }
76    return self;
77}
78
79- (void) dealloc
80{
81#ifdef DEBUG_DEALLOC
82    NSLog( @"called dealloc in AMutableDictionary" );
83#endif
84    if ( data ) [data release];
85    if ( root ) [root release];
86    [super dealloc];
87}
88
89- (id) objectForKey:(id)aKey
90{
91    id obj = nil;
92    ACBTree *node;
93    ACBKey *kp;
94    NSInteger ret;
95    BOOL mustRelease = NO;
96
97    if ( [aKey isKindOfClass:[NSString class]] ) {
98        kp = [ACBKey newKeyWithKStr:aKey];
99        mustRelease = YES;
100    }
101    else if ( [aKey isKindOfClass:[ACBKey class]] ) {
102        kp = aKey;
103        //ACBKey *akey = [ACBKey newKey:aKey];
104    }
105    else {
106        @throw [NSException exceptionWithName:NSInvalidArgumentException
107                                       reason:[NSString stringWithFormat:@"What kind of key is this? %@", aKey]
108                                     userInfo:nil];
109        return nil; // not a key that I know how to deal with
110    }
111    node = [root search:kp.key];
112    if ( node != nil ) {
113        ret = [node searchnode:kp.key match:YES];
114        if ( ret >= 0 && ret < node.numkeys ) {
115            obj = node.btNodes[ret];
116            if ( obj == [NSNull null] ) {
117                obj = nil;
118            }
119        }
120    }
121    if ( mustRelease ) [kp release];
122    return obj;
123}
124
125- (void) setObject:(id)obj forKey:(id)aKey
126{
127    ACBKey *kp;
128    BOOL mustRelease = NO;
129    if ( [aKey isKindOfClass:[NSString class]] ) {
130        kp = [ACBKey newKeyWithKStr:aKey];
131        mustRelease = YES;
132    }
133    else if ( [aKey isKindOfClass:[ACBKey class]] ) {
134        kp = (ACBKey *)aKey;
135    }
136    else {
137        @throw [NSException exceptionWithName:NSInvalidArgumentException
138                                       reason:[NSString stringWithFormat:@"What kind of key is this? %@", aKey]
139                                     userInfo:nil];
140    }
141    if ( [root search:kp.key] == nil ) {
142        if ( obj == nil ) {
143            obj = [NSNull null];
144        }
145        root = [root insertkey:kp value:obj];
146        [kp retain];
147        [obj retain];
148        kp.recnum = count++;
149    }
150    else {
151        if ( mustRelease ) [kp release];
152        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"key alreadyExists" userInfo:nil];
153    }
154    return;
155}
156
157- (BOOL) isEqual:(id)object
158{
159    return [super isEqual:object];
160}
161
162- (void) removeObjectForKey:(id)aKey
163{
164    if ( [root deletekey:aKey] == SUCCESS )
165        count--;
166}
167
168- (NSUInteger) count
169{
170    return count;
171}
172
173- (NSArray *) allKeys
174{
175    NSUInteger cnt = [root keyWalkLeaves];
176    return [NSArray arrayWithObjects:ptrBuffer count:cnt];
177}
178
179- (NSArray *) allValues
180{
181    NSUInteger cnt = [root objectWalkLeaves];
182    return [NSArray arrayWithObjects:ptrBuffer count:cnt];
183}
184
185- (ArrayIterator *) keyEnumerator
186{
187    return [ArrayIterator newIterator:[self allKeys]];
188}
189
190- (ArrayIterator *) objectEnumerator
191{
192    return [ArrayIterator newIterator:[self allValues]];
193}
194
195// This is where all the magic happens.
196// You have two choices when implementing this method:
197// 1) Use the stack based array provided by stackbuf. If you do this, then you must respect the value of 'len'.
198// 2) Return your own array of objects. If you do this, return the full length of the array returned until you run out of objects, then return 0. For example, a linked-array implementation may return each array in order until you iterate through all arrays.
199// In either case, state->itemsPtr MUST be a valid array (non-nil). This sample takes approach #1, using stackbuf to store results.
200- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
201{
202    NSUInteger cnt = 0;
203    // This is the initialization condition, so we'll do one-time setup here.
204    // Ensure that you never set state->state back to 0, or use another method to detect initialization
205    // (such as using one of the values of state->extra).
206    if (state->state == 0) {
207        // We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values,
208        // since these values are not otherwise used by the protocol.
209        // If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated.
210        // state->mutationsPtr MUST NOT be NULL.
211        state->mutationsPtr = &state->extra[0];
212        [self.root objectWalkLeaves];
213    }
214    // Now we provide items, which we track with state->state, and determine if we have finished iterating.
215    if (state->state < self.count) {
216        // Set state->itemsPtr to the provided buffer.
217        // Alternate implementations may set state->itemsPtr to an internal C array of objects.
218        // state->itemsPtr MUST NOT be NULL.
219        state->itemsPtr = stackbuf;
220        // Fill in the stack array, either until we've provided all items from the list
221        // or until we've provided as many items as the stack based buffer will hold.
222        while((state->state < self.count) && (cnt < len)) {
223            // For this sample, we generate the contents on the fly.
224            // A real implementation would likely just be copying objects from internal storage.
225            stackbuf[cnt++] = ptrBuffer[state->state++];
226        }
227        // state->state = ((cnt < len)? cnt : len);
228    }
229    else
230    {
231        // We've already provided all our items, so we signal we are done by returning 0.
232        cnt = 0;
233    }
234    return cnt;
235}
236
237- (void) clear
238{
239    if ( count ) [self removeAllObjects];
240}
241
242- (void) removeAllObjects
243{
244    root = [ACBTree newNodeWithDictionary:self];
245    root.nodeid = 0;
246    nxt_nodeid = 1;
247}
248
249- (NSInteger) nextNodeId
250{
251    return nxt_nodeid++;
252}
253
254- (NSArray *) toKeyArray
255{
256    return nil;
257}
258
259- (NSArray *) toValueArray
260{
261    return nil;
262}
263
264@end
265