Home | History | Annotate | Download | only in Framework
      1 #import <Foundation/Foundation.h>
      2 #import "AMutableArray.h"
      3 #import "LinkedHashMap.h"
      4 #import "RuntimeException.h"
      5 
      6 extern NSInteger const DEFAULT_INITIAL_CAPACITY;
      7 extern float const DEFAULT_LOAD_FACTOR;
      8 
      9 @implementation LHMEntry
     10 
     11 @synthesize before;
     12 @synthesize after;
     13 @synthesize accessOrder;
     14 
     15 - (id) newEntry:(NSInteger)aHash key:(NSString *)aKey value:(id)aValue next:(LHMEntry *)aNext
     16 {
     17     return [[LHMEntry alloc] init:aHash key:aKey value:aValue next:aNext];
     18 }
     19 
     20 - (id) init:(NSInteger)aHash key:(NSString *)aKey value:(id)aValue next:(LHMEntry *)aNext
     21 {
     22     self = [super init:aHash key:aKey value:aValue next:aNext];
     23     if (self) {
     24     }
     25     return self;
     26 }
     27 
     28 
     29 - (void) dealloc
     30 {
     31     [before release];
     32     [after release];
     33     [super dealloc];
     34 }
     35 
     36 /**
     37  * Removes this entry from the linked list.
     38  */
     39 - (void) removeEntry
     40 {
     41     before.after = after;
     42     after.before = before;
     43 }
     44 
     45 
     46 /**
     47  * Inserts this entry before the specified existing entry in the list.
     48  */
     49 - (void) addBefore:(LHMEntry *)existingEntry
     50 {
     51     after = [existingEntry retain];
     52     before = [existingEntry.before retain];
     53     before.after = [self retain];
     54     after.before = [self retain];
     55 }
     56 
     57 
     58 /**
     59  * This method is invoked by the superclass whenever the value
     60  * of a pre-existing entry is read by Map.get or modified by Map.set.
     61  * If the enclosing Map is access-ordered, it moves the entry
     62  * to the end of the list; otherwise, it does nothing.
     63  */
     64 - (void) recordAccess:(LinkedHashMap *)m
     65 {
     66     LinkedHashMap *lhm = (LinkedHashMap *)m;
     67     if (lhm.accessOrder) {
     68         lhm.modCount++;
     69         [self removeEntry];
     70         [self addBefore:lhm.header];
     71     }
     72 }
     73 
     74 - (void) recordRemoval:(LinkedHashMap *)m
     75 {
     76     [self removeEntry];
     77 }
     78 
     79 @end
     80 
     81 @implementation LinkedHashIterator
     82 
     83 @synthesize nextEntry;
     84 @synthesize lastReturned;
     85 @synthesize lhm;
     86 
     87 + (LinkedHashIterator *) newIterator:(LinkedHashMap *)aLHM
     88 {
     89     return [[LinkedHashIterator alloc] init:aLHM];
     90 }
     91 
     92 - (id) init:(LinkedHashMap *)aLHM
     93 {
     94     self = [super init];
     95     if ( self ) {
     96         lhm = aLHM;
     97         nextEntry = lhm.header.after;
     98         lastReturned = nil;
     99         expectedModCount = lhm.modCount;
    100 /*
    101         AMutableArray *a = [AMutableArray arrayWithCapacity:lhm.Capacity];
    102         LHMEntry *tmp = lhm.header.after;
    103         while ( tmp != lhm.header ) {
    104             [a addObject:tmp];
    105             tmp = tmp.after;
    106         }
    107         anArray = [NSArray arrayWithArray:a];
    108  */
    109     }
    110     return self;
    111 }
    112 
    113 - (BOOL) hasNext
    114 {
    115     return nextEntry != lhm.header;
    116 }
    117 
    118 - (void) remove
    119 {
    120     if (lastReturned == nil)
    121         @throw [[IllegalStateException newException] autorelease];
    122     if (lhm.modCount != expectedModCount)
    123         @throw [[ConcurrentModificationException newException:@"Unexpected modCount"] autorelease];
    124     [lhm remove:(NSString *)(lastReturned.key)];
    125     lastReturned = nil;
    126     expectedModCount = lhm.modCount;
    127 }
    128 
    129 - (LHMEntry *) nextEntry
    130 {
    131     if (lhm.modCount != expectedModCount)
    132         @throw [[ConcurrentModificationException newException:@"Unexpected modCount"] autorelease];
    133     if (nextEntry == lhm.header)
    134         @throw [[[NoSuchElementException alloc] init] autorelease];
    135     LHMEntry * e = lastReturned = nextEntry;
    136     nextEntry = e.after;
    137     return e;
    138 }
    139 
    140 - (void) dealloc
    141 {
    142     [nextEntry release];
    143     [lastReturned release];
    144     [super dealloc];
    145 }
    146 
    147 @end
    148 
    149 @implementation LHMKeyIterator
    150 + (LHMKeyIterator *)newIterator:(LinkedHashMap *)aLHM
    151 {
    152     return [[LHMKeyIterator alloc] init:aLHM];
    153 }
    154 
    155 - (id) init:(LinkedHashMap *)aLHM
    156 {
    157     self = [super init:aLHM];
    158     if ( self ) {
    159     }
    160     return self;
    161 }
    162 
    163 - (NSString *) next
    164 {
    165     return [self nextEntry].key;
    166 }
    167 
    168 @end
    169 
    170 @implementation LHMValueIterator
    171 + (LHMValueIterator *)newIterator:(LinkedHashMap *)aLHM
    172 {
    173     return [[LHMValueIterator alloc] init:aLHM];
    174 }
    175 
    176 - (id) init:(LinkedHashMap *)aLHM
    177 {
    178     self = [super init:aLHM];
    179     if ( self ) {
    180     }
    181     return self;
    182 }
    183 
    184 - (id) next
    185 {
    186     return [self nextEntry].value;
    187 }
    188 
    189 @end
    190 
    191 @implementation LHMEntryIterator
    192 + (LHMEntryIterator *)newIterator:(LinkedHashMap *)aLHM
    193 {
    194     return [[LHMEntryIterator alloc] init:aLHM];
    195 }
    196 
    197 - (id) init:(LinkedHashMap *)aLHM
    198 {
    199     self = [super init:aLHM];
    200     if ( self ) {
    201     }
    202     return self;
    203 }
    204 
    205 - (LHMEntry *) next
    206 {
    207     return [self nextEntry];
    208 }
    209 
    210 @end
    211 
    212 //long const serialVersionUID = 3801124242820219131L;
    213 
    214 @implementation LinkedHashMap
    215 
    216 @synthesize header;
    217 @synthesize accessOrder;
    218 
    219 /**
    220  * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
    221  * with the specified initial capacity and load factor.
    222  * 
    223  * @param  initialCapacity the initial capacity
    224  * @param  loadFactor      the load factor
    225  * @throws IllegalArgumentException if the initial capacity is negative
    226  * or the load factor is nonpositive
    227  */
    228 + (id) newLinkedHashMap:(NSInteger)anInitialCapacity
    229              loadFactor:(float)loadFactor
    230             accessOrder:(BOOL)anAccessOrder
    231 {
    232     return [[LinkedHashMap alloc] init:anInitialCapacity
    233                             loadFactor:loadFactor
    234                            accessOrder:(BOOL)anAccessOrder];
    235 }
    236 
    237 + (id) newLinkedHashMap:(NSInteger)anInitialCapacity loadFactor:(float)loadFactor
    238 {
    239     return [[LinkedHashMap alloc] init:anInitialCapacity loadFactor:loadFactor];
    240 }
    241 
    242 + (id) newLinkedHashMap:(NSInteger)anInitialCapacity
    243 {
    244     return [[LinkedHashMap alloc] init:anInitialCapacity loadFactor:DEFAULT_LOAD_FACTOR];
    245 }
    246 
    247 /**
    248  * Constructs an empty <tt>LinkedHashMap</tt> instance with the
    249  * specified initial capacity, load factor and ordering mode.
    250  * 
    251  * @param  initialCapacity the initial capacity
    252  * @param  loadFactor      the load factor
    253  * @param  accessOrder     the ordering mode - <tt>true</tt> for
    254  * access-order, <tt>false</tt> for insertion-order
    255  * @throws IllegalArgumentException if the initial capacity is negative
    256  * or the load factor is nonpositive
    257  */
    258 - (id) init:(NSInteger)anInitialCapacity loadFactor:(float)aLoadFactor accessOrder:(BOOL)anAccessOrder
    259 {
    260     self = [super init:anInitialCapacity loadFactor:aLoadFactor];
    261     if ( self ) {
    262         accessOrder = anAccessOrder;
    263         header = [[[LHMEntry alloc] init:-1 key:nil value:nil next:nil] retain];
    264         header.before = header.after = header;
    265     }
    266     return self;
    267 }
    268 
    269 - (id) init:(NSInteger)anInitialCapacity loadFactor:(float)aLoadFactor
    270 {
    271     self = [super init:anInitialCapacity loadFactor:aLoadFactor];
    272     if ( self ) {
    273         accessOrder = NO;
    274         header = [[[LHMEntry alloc] init:-1 key:nil value:nil next:nil] retain];
    275         header.before = header.after = header;
    276     }
    277     return self;
    278 }
    279 
    280 /**
    281  * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
    282  * with the specified initial capacity and a default load factor (0.75).
    283  * 
    284  * @param  initialCapacity the initial capacity
    285  * @throws IllegalArgumentException if the initial capacity is negative
    286  */
    287 - (id) init:(NSInteger)initialCapacity
    288 {
    289     self = [super init:initialCapacity loadFactor:DEFAULT_LOAD_FACTOR];
    290     if ( self ) {
    291         accessOrder = NO;
    292         header = [[[LHMEntry alloc] init:-1 key:nil value:nil next:nil] retain];
    293         header.before = header.after = header;
    294     }
    295     return self;
    296 }
    297 
    298 /**
    299  * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with
    300  * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
    301  * instance is created with a default load factor (0.75) and an initial
    302  * capacity sufficient to hold the mappings in the specified map.
    303  * 
    304  * @param  m the map whose mappings are to be placed in this map
    305  * @throws NullPointerException if the specified map is null
    306  */
    307 - (id) initWithM:(LinkedHashMap *)m
    308 {
    309     self = [super initWithM:m];
    310     if ( self ) {
    311         accessOrder = NO;
    312         header = [[[LHMEntry alloc] init:-1 key:nil value:nil next:nil] retain];
    313         header.before = header.after = header;
    314     }
    315     return self;
    316 }
    317 
    318 /**
    319  * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
    320  * with the default initial capacity (16) and load factor (0.75).
    321  */
    322 - (id) init
    323 {
    324     self = [super init];
    325     if ( self ) {
    326         accessOrder = NO;
    327         header = [[[LHMEntry alloc] init:-1 key:nil value:nil next:nil] retain];
    328         header.before = header.after = header;
    329     }
    330     return self;
    331 }
    332 
    333 
    334 /**
    335  * Transfers all entries to new table array.  This method is called
    336  * by superclass resize.  It is overridden for performance, as it is
    337  * faster to iterate using our linked list.
    338  */
    339 - (void) transfer:(AMutableArray *)newTable
    340 {
    341     NSInteger newCapacity = [newTable count];
    342     
    343     for (LHMEntry * e = header.after; e != header; e = e.after) {
    344         NSInteger index = [self indexFor:e.hash length:newCapacity];
    345         e.next = [newTable objectAtIndex:index];
    346         [newTable replaceObjectAtIndex:index withObject:e];
    347     }
    348     
    349 }
    350 
    351 /**
    352  * Returns <tt>true</tt> if this map maps one or more keys to the
    353  * specified value.
    354  * 
    355  * @param value value whose presence in this map is to be tested
    356  * @return <tt>true</tt> if this map maps one or more keys to the
    357  * specified value
    358  */
    359 - (BOOL) containsValue:(id)value
    360 {
    361     if (value == nil) {
    362         
    363         for (LHMEntry * e = header.after; e != header; e = e.after)
    364             if (e.value == nil)
    365                 return YES;
    366         
    367     }
    368     else {
    369         
    370         for (LHMEntry * e = header.after; e != header; e = e.after)
    371             if ([value isEqualTo:e.value])
    372                 return YES;
    373         
    374     }
    375     return NO;
    376 }
    377 
    378 /**
    379  * Returns the value to which the specified key is mapped,
    380  * or {@code null} if this map contains no mapping for the key.
    381  * 
    382  * <p>More formally, if this map contains a mapping from a key
    383  * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
    384  * key.equals(k))}, then this method returns {@code v}; otherwise
    385  * it returns {@code null}.  (There can be at most one such mapping.)
    386  * 
    387  * <p>A return value of {@code null} does not <i>necessarily</i>
    388  * indicate that the map contains no mapping for the key; it's also
    389  * possible that the map explicitly maps the key to {@code null}.
    390  * The {@link #containsKey containsKey} operation may be used to
    391  * distinguish these two cases.
    392  */
    393 - (id) get:(NSString *)aKey
    394 {
    395     LHMEntry * e = (LHMEntry *)[self getEntry:aKey];
    396     if (e == nil)
    397         return nil;
    398     [e recordAccess:self];
    399     return e.value;
    400 }
    401 
    402 
    403 /**
    404  * Removes all of the mappings from this map.
    405  * The map will be empty after this call returns.
    406  */
    407 - (void) clear
    408 {
    409     [super clear];
    410     header.before = header.after = header;
    411 }
    412 
    413 - (void) dealloc {
    414     [header release];
    415     [super dealloc];
    416 }
    417 
    418 - (LHMEntryIterator *) newEntryIterator
    419 {
    420     return [LHMEntryIterator newIterator:self];
    421 }
    422 
    423 - (LHMKeyIterator *) newKeyIterator
    424 {
    425     return [LHMKeyIterator newIterator:self];
    426 }
    427 
    428 - (LHMValueIterator *) newValueIterator
    429 {
    430     return [LHMValueIterator newIterator:self];
    431 }
    432 
    433 
    434 /**
    435  * This override alters behavior of superclass put method. It causes newly
    436  * allocated entry to get inserted at the end of the linked list and
    437  * removes the eldest entry if appropriate.
    438  */
    439 - (void) addEntry:(NSInteger)aHash key:(NSString *)aKey value:(id)aValue bucketIndex:(NSInteger)aBucketIndex
    440 {
    441     [self createEntry:aHash key:aKey value:aValue bucketIndex:aBucketIndex];
    442     LHMEntry * eldest = header.after;
    443     if ([self removeEldestEntry:eldest]) {
    444         [self removeEntryForKey:eldest.key];
    445     }
    446     else {
    447         if (count >= threshold)
    448             [self resize:2 * [buffer length]];
    449     }
    450 }
    451 
    452 
    453 /**
    454  * This override differs from addEntry in that it doesn't resize the
    455  * table or remove the eldest entry.
    456  */
    457 - (void) createEntry:(NSInteger)aHash key:(NSString *)aKey value:(id)aValue bucketIndex:(NSInteger)bucketIndex
    458 {
    459     LHMEntry *old = (LHMEntry *)ptrBuffer[bucketIndex];
    460     LHMEntry *e = [[[LHMEntry alloc] init:aHash key:aKey value:aValue next:old] retain];
    461     ptrBuffer[bucketIndex] = (id)e;
    462     [e addBefore:header];
    463     count++;
    464 }
    465 
    466 
    467 /**
    468  * Returns <tt>true</tt> if this map should remove its eldest entry.
    469  * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
    470  * inserting a new entry into the map.  It provides the implementor
    471  * with the opportunity to remove the eldest entry each time a new one
    472  * is added.  This is useful if the map represents a cache: it allows
    473  * the map to reduce memory consumption by deleting stale entries.
    474  * 
    475  * <p>Sample use: this override will allow the map to grow up to 100
    476  * entries and then delete the eldest entry each time a new entry is
    477  * added, maintaining a steady state of 100 entries.
    478  * <pre>
    479  * private static final NSInteger MAX_ENTRIES = 100;
    480  * 
    481  * protected boolean removeEldestEntry(Map.LHMEntry eldest) {
    482  * return count() > MAX_ENTRIES;
    483  * }
    484  * </pre>
    485  * 
    486  * <p>This method typically does not modify the map in any way,
    487  * instead allowing the map to modify itself as directed by its
    488  * return value.  It <i>is</i> permitted for this method to modify
    489  * the map directly, but if it does so, it <i>must</i> return
    490  * <tt>false</tt> (indicating that the map should not attempt any
    491  * further modification).  The effects of returning <tt>true</tt>
    492  * after modifying the map from within this method are unspecified.
    493  * 
    494  * <p>This implementation merely returns <tt>false</tt> (so that this
    495  * map acts like a normal map - the eldest element is never removed).
    496  * 
    497  * @param    eldest The least recently inserted entry in the map, or if
    498  * this is an access-ordered map, the least recently accessed
    499  * entry.  This is the entry that will be removed it this
    500  * method returns <tt>true</tt>.  If the map was empty prior
    501  * to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
    502  * in this invocation, this will be the entry that was just
    503  * inserted; in other words, if the map contains a single
    504  * entry, the eldest entry is also the newest.
    505  * @return   <tt>true</tt> if the eldest entry should be removed
    506  * from the map; <tt>false</tt> if it should be retained.
    507  */
    508 - (BOOL) removeEldestEntry:(LHMEntry *)eldest
    509 {
    510     return NO;
    511 }
    512 
    513 @end
    514