From 23cdee2f22af61e968334a9d0e1c034ee61b2f12 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Fri, 29 Aug 2025 21:39:04 +0300 Subject: [PATCH 1/9] new: CPMapTable.j --- Foundation/CPMapTable.j | 133 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Foundation/CPMapTable.j diff --git a/Foundation/CPMapTable.j b/Foundation/CPMapTable.j new file mode 100644 index 000000000..e8121b33d --- /dev/null +++ b/Foundation/CPMapTable.j @@ -0,0 +1,133 @@ +@import "CPObject.j" +@import "CPEnumerator.j" +@import "CPDictionary.j" + +/*! + @class CPMapTable + @ingroup foundation + A mutable collection modeled after NSDictionary that allows for arbitrary + objects or values to be used as keys. This implementation uses the native + ECMAScript 6 Map object for its underlying storage. + */ +@implementation CPMapTable : CPObject +{ + // The underlying ES6 Map to store the key-value pairs. + id _map; +} + +/*! + Initializes a new, empty CPMapTable. + */ +- (id)init +{ + if (self = [super init]) + { + _map = new Map(); + } + + return self; +} + +/*! + Releases the underlying map. + */ +- (void)dealloc +{ + _map = nil; + + [super dealloc]; +} + +#pragma mark - +#pragma mark Accessing Content + +/*! + Returns the value associated with a given key. + @param aKey The key for which to return the corresponding value. + @return The value associated with aKey, or nil if no value is associated with aKey. + */ +- (id)objectForKey:(id)aKey +{ + return _map.get(aKey); +} + +/*! + Returns an enumerator object that lets you access each key in the map table. + @return A CPEnumerator object for the keys in the map table. + */ +- (CPEnumerator)keyEnumerator +{ + return [CPEnumerator enumeratorWithIterator:_map.keys()]; +} + +/*! + Returns an enumerator object that lets you access each value in the map table. + @return A CPEnumerator object for the values in the map table. + */ +- (CPEnumerator)objectEnumerator +{ + return [CPEnumerator enumeratorWithIterator:_map.values()]; +} + +/*! + Returns the number of key-value pairs in the map table. + @return The number of entries in the map table. + */ +- (unsigned int)count +{ + return _map.size; +} + +#pragma mark - +#pragma mark Manipulating Content + +/*! + Adds a given key-value pair to the map table. + If aKey already exists in the map table, anObject takes its place. + @param anObject The value for aKey. + @param aKey The key for anObject. + */ +- (void)setObject:(id)anObject forKey:(id)aKey +{ + _map.set(aKey, anObject); +} + +/*! + Removes a given key and its associated value from the map table. + @param aKey The key to remove. + */ +- (void)removeObjectForKey:(id)aKey +{ + _map.delete(aKey); +} + +/*! + Empties the map table of its entries. + */ +- (void)removeAllObjects +{ + _map.clear(); +} + +#pragma mark - +#pragma mark Creating a Dictionary Representation + +/*! + Returns a dictionary representation of the map table. + Note: This will only work correctly if all keys are strings. + + @return A CPDictionary containing the entries of the map table. + */ +- (CPDictionary)dictionaryRepresentation +{ + var dictionary = [CPDictionary dictionary]; + + for (var [key, value] of _map.entries()) + { + [dictionary setObject:value forKey:key]; + } + + return dictionary; +} + +@end From 1112e658d40d97a2440683b1aa222d9d5ea8dea6 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:22:31 +0300 Subject: [PATCH 2/9] removed: dealloc --- Foundation/CPMapTable.j | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Foundation/CPMapTable.j b/Foundation/CPMapTable.j index e8121b33d..6424f7ef4 100644 --- a/Foundation/CPMapTable.j +++ b/Foundation/CPMapTable.j @@ -28,16 +28,6 @@ return self; } -/*! - Releases the underlying map. - */ -- (void)dealloc -{ - _map = nil; - - [super dealloc]; -} - #pragma mark - #pragma mark Accessing Content From 799cb189c97ca2fb8ceda891a3492054ce878641 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:28:14 +0300 Subject: [PATCH 3/9] new: test cases --- Tests/Foundation/CPMapTableTest.j | 141 ++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Tests/Foundation/CPMapTableTest.j diff --git a/Tests/Foundation/CPMapTableTest.j b/Tests/Foundation/CPMapTableTest.j new file mode 100644 index 000000000..f29144c89 --- /dev/null +++ b/Tests/Foundation/CPMapTableTest.j @@ -0,0 +1,141 @@ +@import +@import +@import +@import + +@implementation CPMapTableTest : OJTestCase +{ + CPMapTable map_table; + id objectKey; +} + +- (void)setUp +{ + map_table = [[CPMapTable alloc] init]; + objectKey = [[CPObject alloc] init]; +} + +- (void)testInit +{ + [self assert:[map_table count] equals:0]; +} + +- (void)testSetObjectForKey +{ + [map_table setObject:@"value1" forKey:@"key1"]; + [self assert:[map_table count] equals:1]; + [self assert:[map_table objectForKey:@"key1"] equals:@"value1"]; + + [map_table setObject:@"value2" forKey:123]; + [self assert:[map_table count] equals:2]; + [self assert:[map_table objectForKey:123] equals:@"value2"]; + + [map_table setObject:@"value3" forKey:objectKey]; + [self assert:[map_table count] equals:3]; + [self assert:[map_table objectForKey:objectKey] equals:@"value3"]; + + // Test overriding a value + [map_table setObject:@"newValue" forKey:@"key1"]; + [self assert:[map_table count] equals:3]; + [self assert:[map_table objectForKey:@"key1"] equals:@"newValue"]; +} + +- (void)testObjectForKey +{ + [self assertNull:[map_table objectForKey:@"nonExistentKey"]]; + + [map_table setObject:@"value1" forKey:@"key1"]; + [self assert:[map_table objectForKey:@"key1"] equals:@"value1"]; +} + +- (void)testCount +{ + [self assert:[map_table count] equals:0]; + + [map_table setObject:@"value1" forKey:@"key1"]; + [self assert:[map_table count] equals:1]; + + [map_table setObject:@"value2" forKey:@"key2"]; + [self assert:[map_table count] equals:2]; + + [map_table removeObjectForKey:@"key1"]; + [self assert:[map_table count] equals:1]; +} + +- (void)testRemoveObjectForKey +{ + [map_table setObject:@"value1" forKey:@"key1"]; + [map_table setObject:@"value2" forKey:123]; + + [map_table removeObjectForKey:@"key1"]; + [self assert:[map_table count] equals:1]; + [self assertNull:[map_table objectForKey:@"key1"]]; + [self assert:[map_table objectForKey:123] equals:@"value2"]; + + [map_table removeObjectForKey:123]; + [self assert:[map_table count] equals:0]; + [self assertNull:[map_table objectForKey:123]]; +} + +- (void)testRemoveAllObjects +{ + [map_table setObject:@"value1" forKey:@"key1"]; + [map_table setObject:@"value2" forKey:@"key2"]; + + [map_table removeAllObjects]; + [self assert:[map_table count] equals:0]; + [self assertNull:[map_table objectForKey:@"key1"]]; + [self assertNull:[map_table objectForKey:@"key2"]]; +} + +- (void)testKeyEnumerator +{ + var enumerator = [map_table keyEnumerator]; + [self assertNull:[enumerator nextObject]]; + [self assert:[[enumerator allObjects] count] equals:0]; + + + [map_table setObject:@"value1" forKey:@"key1"]; + [map_table setObject:@"value2" forKey:123]; + [map_table setObject:@"value3" forKey:objectKey]; + + enumerator = [map_table keyEnumerator]; + var allKeys = [enumerator allObjects]; + + [self assert:[allKeys count] equals:3]; + [self assertTrue:[allKeys containsObject:@"key1"]]; + [self assertTrue:[allKeys containsObject:123]]; + [self assertTrue:[allKeys containsObject:objectKey]]; +} + +- (void)testObjectEnumerator +{ + var enumerator = [map_table objectEnumerator]; + [self assertNull:[enumerator nextObject]]; + [self assert:[[enumerator allObjects] count] equals:0]; + + [map_table setObject:@"value1" forKey:@"key1"]; + [map_table setObject:@"value2" forKey:123]; + [map_table setObject:@"value3" forKey:objectKey]; + + enumerator = [map_table objectEnumerator]; + var allValues = [enumerator allObjects]; + + [self assert:[allValues count] equals:3]; + [self assertTrue:[allValues containsObject:@"value1"]]; + [self assertTrue:[allValues containsObject:@"value2"]]; + [self assertTrue:[allValues containsObject:@"value3"]]; +} + +- (void)testDictionaryRepresentation +{ + [map_table setObject:@"value1" forKey:@"key1"]; + [map_table setObject:@"value2" forKey:@"key2"]; + + var dictionary = [map_table dictionaryRepresentation]; + [self assert:[dictionary count] equals:2]; + [self assert:[dictionary objectForKey:@"key1"] equals:@"value1"]; + [self assert:[dictionary objectForKey:@"key2"] equals:@"value2"]; +} + +@end \ No newline at end of file From 18bb6daf1061fad4ca639493202bc943c1d5dc52 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:34:02 +0300 Subject: [PATCH 4/9] fixed: nil and operator issues --- Foundation/CPMapTable.j | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Foundation/CPMapTable.j b/Foundation/CPMapTable.j index 6424f7ef4..35491f4cd 100644 --- a/Foundation/CPMapTable.j +++ b/Foundation/CPMapTable.j @@ -1,6 +1,7 @@ @import "CPObject.j" @import "CPEnumerator.j" @import "CPDictionary.j" +@import "CPArray.j" /*! @class CPMapTable @@ -38,7 +39,8 @@ */ - (id)objectForKey:(id)aKey { - return _map.get(aKey); + var value = _map.get(aKey); + return value === undefined ? nil : value; } /*! @@ -47,7 +49,7 @@ */ - (CPEnumerator)keyEnumerator { - return [CPEnumerator enumeratorWithIterator:_map.keys()]; + return [[CPArray arrayWithJSArray:[..._map.keys()]] objectEnumerator]; } /*! @@ -56,7 +58,7 @@ */ - (CPEnumerator)objectEnumerator { - return [CPEnumerator enumeratorWithIterator:_map.values()]; + return [[CPArray arrayWithJSArray:[..._map.values()]] objectEnumerator]; } /*! From 42a00edbd89a1ec0143103f3d428677c8184a2fd Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:35:53 +0300 Subject: [PATCH 5/9] fixed: objectEnumerator --- Foundation/CPMapTable.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/CPMapTable.j b/Foundation/CPMapTable.j index 35491f4cd..57f6ba3b8 100644 --- a/Foundation/CPMapTable.j +++ b/Foundation/CPMapTable.j @@ -58,7 +58,7 @@ */ - (CPEnumerator)objectEnumerator { - return [[CPArray arrayWithJSArray:[..._map.values()]] objectEnumerator]; + return [[..._map.values()] objectEnumerator]; } /*! From c56448a29c373294d591e524b8d0220a2c45cc9a Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:38:00 +0300 Subject: [PATCH 6/9] fixed: keyEnumerator --- Foundation/CPMapTable.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/CPMapTable.j b/Foundation/CPMapTable.j index 57f6ba3b8..9399c347c 100644 --- a/Foundation/CPMapTable.j +++ b/Foundation/CPMapTable.j @@ -49,7 +49,7 @@ */ - (CPEnumerator)keyEnumerator { - return [[CPArray arrayWithJSArray:[..._map.keys()]] objectEnumerator]; + return [[..._map.keys()] objectEnumerator]; } /*! From b2b29c928fd7c868601137ab6a2f5f16262cef58 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:41:34 +0300 Subject: [PATCH 7/9] fixed: testcases --- Tests/Foundation/CPMapTableTest.j | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Tests/Foundation/CPMapTableTest.j b/Tests/Foundation/CPMapTableTest.j index f29144c89..98fc4b510 100644 --- a/Tests/Foundation/CPMapTableTest.j +++ b/Tests/Foundation/CPMapTableTest.j @@ -90,17 +90,16 @@ - (void)testKeyEnumerator { - var enumerator = [map_table keyEnumerator]; - [self assertNull:[enumerator nextObject]]; - [self assert:[[enumerator allObjects] count] equals:0]; - + // Test empty case by creating a new enumerator for each assertion + [self assertNull:[[map_table keyEnumerator] nextObject]]; + [self assert:[[[map_table keyEnumerator] allObjects] count] equals:0]; + // Add objects and test populated case [map_table setObject:@"value1" forKey:@"key1"]; [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; - enumerator = [map_table keyEnumerator]; - var allKeys = [enumerator allObjects]; + var allKeys = [[map_table keyEnumerator] allObjects]; [self assert:[allKeys count] equals:3]; [self assertTrue:[allKeys containsObject:@"key1"]]; @@ -110,16 +109,16 @@ - (void)testObjectEnumerator { - var enumerator = [map_table objectEnumerator]; - [self assertNull:[enumerator nextObject]]; - [self assert:[[enumerator allObjects] count] equals:0]; + // Test empty case by creating a new enumerator for each assertion + [self assertNull:[[map_table objectEnumerator] nextObject]]; + [self assert:[[[map_table objectEnumerator] allObjects] count] equals:0]; + // Add objects and test populated case [map_table setObject:@"value1" forKey:@"key1"]; [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; - enumerator = [map_table objectEnumerator]; - var allValues = [enumerator allObjects]; + var allValues = [[map_table objectEnumerator] allObjects]; [self assert:[allValues count] equals:3]; [self assertTrue:[allValues containsObject:@"value1"]]; @@ -138,4 +137,4 @@ [self assert:[dictionary objectForKey:@"key2"] equals:@"value2"]; } -@end \ No newline at end of file +@end From dfbf07b10c4cc54befa24cc434a418e541e99cb8 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:44:39 +0300 Subject: [PATCH 8/9] fixed: test suite --- Tests/Foundation/CPMapTableTest.j | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Tests/Foundation/CPMapTableTest.j b/Tests/Foundation/CPMapTableTest.j index 98fc4b510..085bcd709 100644 --- a/Tests/Foundation/CPMapTableTest.j +++ b/Tests/Foundation/CPMapTableTest.j @@ -88,13 +88,15 @@ [self assertNull:[map_table objectForKey:@"key2"]]; } -- (void)testKeyEnumerator +- (void)testKeyEnumeratorOnEmptyMap { - // Test empty case by creating a new enumerator for each assertion + [self assert:[map_table count] equals:0 message:@"Pre-condition failed: map should be empty."]; [self assertNull:[[map_table keyEnumerator] nextObject]]; [self assert:[[[map_table keyEnumerator] allObjects] count] equals:0]; +} - // Add objects and test populated case +- (void)testKeyEnumeratorOnPopulatedMap +{ [map_table setObject:@"value1" forKey:@"key1"]; [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; @@ -107,13 +109,15 @@ [self assertTrue:[allKeys containsObject:objectKey]]; } -- (void)testObjectEnumerator +- (void)testObjectEnumeratorOnEmptyMap { - // Test empty case by creating a new enumerator for each assertion + [self assert:[map_table count] equals:0 message:@"Pre-condition failed: map should be empty."]; [self assertNull:[[map_table objectEnumerator] nextObject]]; [self assert:[[[map_table objectEnumerator] allObjects] count] equals:0]; +} - // Add objects and test populated case +- (void)testObjectEnumeratorOnPopulatedMap +{ [map_table setObject:@"value1" forKey:@"key1"]; [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; From c14e08633dea1650e8865ac6de58e4bbfda9f97d Mon Sep 17 00:00:00 2001 From: daboe01 Date: Tue, 2 Sep 2025 15:51:40 +0300 Subject: [PATCH 9/9] fixed: testcase --- Tests/Foundation/CPMapTableTest.j | 33 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/Tests/Foundation/CPMapTableTest.j b/Tests/Foundation/CPMapTableTest.j index 085bcd709..d5c7864d8 100644 --- a/Tests/Foundation/CPMapTableTest.j +++ b/Tests/Foundation/CPMapTableTest.j @@ -1,6 +1,7 @@ @import @import @import +@import @import @implementation CPMapTableTest : OJTestCase @@ -92,7 +93,6 @@ { [self assert:[map_table count] equals:0 message:@"Pre-condition failed: map should be empty."]; [self assertNull:[[map_table keyEnumerator] nextObject]]; - [self assert:[[[map_table keyEnumerator] allObjects] count] equals:0]; } - (void)testKeyEnumeratorOnPopulatedMap @@ -101,19 +101,23 @@ [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; - var allKeys = [[map_table keyEnumerator] allObjects]; + var foundKeys = [CPMutableArray array], + enumerator = [map_table keyEnumerator], + aKey; - [self assert:[allKeys count] equals:3]; - [self assertTrue:[allKeys containsObject:@"key1"]]; - [self assertTrue:[allKeys containsObject:123]]; - [self assertTrue:[allKeys containsObject:objectKey]]; + while (aKey = [enumerator nextObject]) + [foundKeys addObject:aKey]; + + [self assert:[foundKeys count] equals:3]; + [self assertTrue:[foundKeys containsObject:@"key1"]]; + [self assertTrue:[foundKeys containsObject:123]]; + [self assertTrue:[foundKeys containsObject:objectKey]]; } - (void)testObjectEnumeratorOnEmptyMap { [self assert:[map_table count] equals:0 message:@"Pre-condition failed: map should be empty."]; [self assertNull:[[map_table objectEnumerator] nextObject]]; - [self assert:[[[map_table objectEnumerator] allObjects] count] equals:0]; } - (void)testObjectEnumeratorOnPopulatedMap @@ -122,12 +126,17 @@ [map_table setObject:@"value2" forKey:123]; [map_table setObject:@"value3" forKey:objectKey]; - var allValues = [[map_table objectEnumerator] allObjects]; + var foundValues = [CPMutableArray array], + enumerator = [map_table objectEnumerator], + aValue; - [self assert:[allValues count] equals:3]; - [self assertTrue:[allValues containsObject:@"value1"]]; - [self assertTrue:[allValues containsObject:@"value2"]]; - [self assertTrue:[allValues containsObject:@"value3"]]; + while (aValue = [enumerator nextObject]) + [foundValues addObject:aValue]; + + [self assert:[foundValues count] equals:3]; + [self assertTrue:[foundValues containsObject:@"value1"]]; + [self assertTrue:[foundValues containsObject:@"value2"]]; + [self assertTrue:[foundValues containsObject:@"value3"]]; } - (void)testDictionaryRepresentation