This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 38732083a6 org.apache.juneau.common.reflect API improvements
38732083a6 is described below
commit 38732083a636c3b2b761cadfbde33876586b0c1d
Author: James Bognar <[email protected]>
AuthorDate: Tue Nov 25 09:43:23 2025 -0500
org.apache.juneau.common.reflect API improvements
---
.../apache/juneau/common/collections/Cache.java | 11 +-
.../apache/juneau/common/collections/Cache2.java | 3 +
.../apache/juneau/common/collections/Cache3.java | 3 +
.../apache/juneau/common/collections/Cache4.java | 3 +
.../apache/juneau/common/collections/Cache5.java | 3 +
.../juneau/common/collections/BidiMap_Test.java | 123 +++++++++++++++++++++
.../juneau/common/collections/Cache2_Test.java | 92 +++++++++++++++
.../juneau/common/collections/Cache3_Test.java | 54 +++++++++
.../juneau/common/collections/Cache4_Test.java | 54 +++++++++
.../juneau/common/collections/Cache5_Test.java | 54 +++++++++
.../juneau/common/collections/Cache_Test.java | 91 +++++++++++++++
.../juneau/common/collections/SimpleMap_Test.java | 63 ++++++++++-
12 files changed, 548 insertions(+), 6 deletions(-)
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
index 72e45466cb..a417db4489 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache.java
@@ -529,8 +529,12 @@ public class Cache<K,V> {
* @return The previous value associated with the key, or <jk>null</jk>
if there was no mapping.
*/
public V put(K key, V value) {
- if (value == null)
- return map.remove(wrap(key));
+ if (value == null) {
+ Tuple1<K> wrapped = wrap(key);
+ V result = map.remove(wrapped);
+ wrapperCache.remove(key); // Clean up wrapper cache
+ return result;
+ }
return map.put(wrap(key), value);
}
@@ -595,6 +599,9 @@ public class Cache<K,V> {
* @return <jk>true</jk> if the cache contains the value.
*/
public boolean containsValue(V value) {
+ // ConcurrentHashMap doesn't allow null values, so null can
never be in the cache
+ if (value == null)
+ return false;
return map.containsValue(value);
}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
index d0448bc6de..da1f85619f 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache2.java
@@ -505,6 +505,9 @@ public class Cache2<K1,K2,V> {
* @return <jk>true</jk> if the cache contains the value.
*/
public boolean containsValue(V value) {
+ // ConcurrentHashMap doesn't allow null values, so null can
never be in the cache
+ if (value == null)
+ return false;
return map.containsValue(value);
}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
index 3cdd0a7f21..468775c69a 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache3.java
@@ -331,6 +331,9 @@ public class Cache3<K1,K2,K3,V> {
* @return <jk>true</jk> if the cache contains the value.
*/
public boolean containsValue(V value) {
+ // ConcurrentHashMap doesn't allow null values, so null can
never be in the cache
+ if (value == null)
+ return false;
return map.containsValue(value);
}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
index 0b1f12e48d..7161d0896b 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache4.java
@@ -330,6 +330,9 @@ public class Cache4<K1,K2,K3,K4,V> {
* @return <jk>true</jk> if the cache contains the value.
*/
public boolean containsValue(V value) {
+ // ConcurrentHashMap doesn't allow null values, so null can
never be in the cache
+ if (value == null)
+ return false;
return map.containsValue(value);
}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
index f794ad6ebf..5c04504991 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/Cache5.java
@@ -341,6 +341,9 @@ public class Cache5<K1,K2,K3,K4,K5,V> {
* @return <jk>true</jk> if the cache contains the value.
*/
public boolean containsValue(V value) {
+ // ConcurrentHashMap doesn't allow null values, so null can
never be in the cache
+ if (value == null)
+ return false;
return map.containsValue(value);
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/BidiMap_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/BidiMap_Test.java
index 7f25e3e492..a263709ee4 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/BidiMap_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/BidiMap_Test.java
@@ -346,6 +346,22 @@ class BidiMap_Test extends TestBase {
assertNull(map.getKey(1));
}
+ @Test void a22b_overwriteInBuilder_differentValue() {
+ // Test line 123: overwriting a key with a different value
+ // This should remove the old value from tracking
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key2", 200)
+ .add("key1", 300) // Overwrite key1 with different
value (line 123)
+ .build();
+
+ assertSize(2, map);
+ assertEquals(300, map.get("key1"));
+ assertEquals(200, map.get("key2"));
+ assertEquals("key1", map.getKey(300));
+ assertNull(map.getKey(100)); // Old value should be removed
+ }
+
@Test void a23_duplicateValuesInBuilder() {
// Duplicate values are not allowed
assertThrows(IllegalArgumentException.class, () ->
@@ -356,6 +372,113 @@ class BidiMap_Test extends TestBase {
);
}
+ @Test void a23b_duplicateValuesInBuilder_overwritingKey() {
+ // Test line 127: trying to add a value that's already mapped
to a different key
+ // This should throw IllegalArgumentException
+ // The condition is: values.contains(value) && !
value.equals(existingValue)
+ // where existingValue is the value currently mapped to the key
being added
+ assertThrows(IllegalArgumentException.class, () ->
+ BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key2", 200)
+ .add("key1", 200) // Try to map key1 to 200,
but 200 is already mapped to key2
+ .build()
+ );
+ }
+
+ @Test void a23c_overwriteKeyWithSameValue() {
+ // Test line 127: overwriting a key with the same value should
not throw
+ // The condition is: values.contains(value) && !
value.equals(existingValue)
+ // When value.equals(existingValue), the condition is false, so
assertArg passes
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key2", 200)
+ .add("key1", 100) // Overwrite key1 with the same
value (should be allowed)
+ .build();
+
+ assertSize(2, map);
+ assertEquals(100, map.get("key1"));
+ assertEquals(200, map.get("key2"));
+ }
+
+ @Test void a23d_overwriteKeyWithNewValue_notInValues() {
+ // Test line 127: overwriting a key with a new value not in the
values set
+ // The condition is: values.contains(value) && !
value.equals(existingValue)
+ // When !values.contains(value), the condition is false, so
assertArg passes
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key2", 200)
+ .add("key1", 300) // Overwrite key1 with a new value
not in values set
+ .build();
+
+ assertSize(2, map);
+ assertEquals(300, map.get("key1"));
+ assertEquals(200, map.get("key2"));
+ }
+
+
//====================================================================================================
+ // Constructor coverage - toMap merge function (lines 185-186)
+
//====================================================================================================
+
+ @Test void a24_constructorMergeFunction_duplicateKeysInBuilder() {
+ // Test lines 185-186: The toMap merge function (a, b) -> b
+ // Note: Since the builder uses LinkedHashMap, duplicate keys
are overwritten before
+ // reaching the constructor, so the merge function may not be
called in practice.
+ // However, this test verifies that the constructor correctly
processes entries
+ // and that the merge function is present as a safety measure.
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key1", 200) // Overwrite - LinkedHashMap handles
this
+ .add("key1", 300) // Overwrite again
+ .build();
+
+ // The last value should win (merge function behavior: (a, b)
-> b)
+ assertSize(1, map);
+ assertEquals(300, map.get("key1"));
+ assertEquals("key1", map.getKey(300));
+ }
+
+ @Test void a25_constructorMergeFunction_duplicateValuesInBuilder() {
+ // Test line 186: The reverse map's toMap merge function (a, b)
-> b
+ // Note: The builder prevents duplicate values, so the merge
function may not be called.
+ // However, this test verifies that the constructor correctly
processes entries
+ // and handles value overwrites correctly.
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 100)
+ .add("key2", 200)
+ .add("key1", 300) // Overwrite key1, removing 100 from
reverse map
+ .build();
+
+ // Verify the reverse map correctly reflects the overwrite
+ assertSize(2, map);
+ assertEquals(300, map.get("key1"));
+ assertEquals(200, map.get("key2"));
+ assertEquals("key1", map.getKey(300));
+ assertEquals("key2", map.getKey(200));
+ assertNull(map.getKey(100)); // Old value should be removed
+ }
+
+ @Test void a26_constructorWithNullEntries_filteredCorrectly() {
+ // Test lines 185-186: Verify that null keys and values are
filtered correctly
+ // in both forward and reverse maps during construction
+ var map = BidiMap.<String,Integer>create()
+ .add("key1", 1)
+ .add(null, 2) // Null key - should be filtered
+ .add("key3", null) // Null value - should be filtered
+ .add(null, null) // Both null - should be filtered
+ .add("key5", 5)
+ .build();
+
+ // Only non-null entries should be present
+ assertSize(2, map);
+ assertEquals(1, map.get("key1"));
+ assertEquals(5, map.get("key5"));
+ assertEquals("key1", map.getKey(1));
+ assertEquals("key5", map.getKey(5));
+ assertNull(map.get(null));
+ assertNull(map.getKey(null));
+ }
+
@Test void a24_sizeAfterOperations() {
var map = BidiMap.<String,Integer>create().build();
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache2_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache2_Test.java
index 05405caccc..1ecff25f97 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache2_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache2_Test.java
@@ -414,6 +414,24 @@ class Cache2_Test extends TestBase {
assertEquals("value2", x.get("user", 123, () -> "should not be
called"));
}
+ @Test
+ void i03_put_withNullValue() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ x.put("user", 123, "value1");
+ var previous = x.put("user", 123, null);
+ assertEquals("value1", previous);
+ assertFalse(x.containsKey("user", 123));
+ }
+
+ @Test
+ void i04_put_withNullValue_newKey() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ var previous = x.put("user", 123, null);
+ assertNull(previous);
+ assertFalse(x.containsKey("user", 123));
+ assertTrue(x.isEmpty());
+ }
+
//====================================================================================================
// j - isEmpty() method
//====================================================================================================
@@ -466,6 +484,80 @@ class Cache2_Test extends TestBase {
assertTrue(x.containsKey("user", 123));
}
+
//====================================================================================================
+ // l - remove() method
+
//====================================================================================================
+
+ @Test
+ void l02_remove_existingKey() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ x.put("user", 123, "value1");
+ var removed = x.remove("user", 123);
+ assertEquals("value1", removed);
+ assertFalse(x.containsKey("user", 123));
+ }
+
+ @Test
+ void l03_remove_nonExistentKey() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ var removed = x.remove("user", 123);
+ assertNull(removed);
+ }
+
+
//====================================================================================================
+ // m - containsValue() method
+
//====================================================================================================
+
+ @Test
+ void m02_containsValue_present() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ x.put("user", 123, "value1");
+ x.put("admin", 456, "value2");
+ assertTrue(x.containsValue("value1"));
+ assertTrue(x.containsValue("value2"));
+ assertFalse(x.containsValue("value3"));
+ }
+
+ @Test
+ void m03_containsValue_notPresent() {
+ var x = Cache2.of(String.class, Integer.class,
String.class).build();
+ assertFalse(x.containsValue("value1"));
+ }
+
+
//====================================================================================================
+ // n - logOnExit() builder methods
+
//====================================================================================================
+
+ @Test
+ void n02_logOnExit_withStringId() {
+ var x = Cache2.of(String.class, Integer.class, String.class)
+ .logOnExit("TestCache2")
+ .supplier((k1, k2) -> k1 + ":" + k2)
+ .build();
+ x.get("user", 123);
+ assertSize(1, x);
+ }
+
+ @Test
+ void n03_logOnExit_withBooleanTrue() {
+ var x = Cache2.of(String.class, Integer.class, String.class)
+ .logOnExit(true, "MyCache2")
+ .supplier((k1, k2) -> k1 + ":" + k2)
+ .build();
+ x.get("user", 123);
+ assertSize(1, x);
+ }
+
+ @Test
+ void n04_logOnExit_withBooleanFalse() {
+ var x = Cache2.of(String.class, Integer.class, String.class)
+ .logOnExit(false, "DisabledCache2")
+ .supplier((k1, k2) -> k1 + ":" + k2)
+ .build();
+ x.get("user", 123);
+ assertSize(1, x);
+ }
+
//====================================================================================================
// l - create() static method
//====================================================================================================
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache3_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache3_Test.java
index 774f79e749..2651ee6efa 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache3_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache3_Test.java
@@ -223,6 +223,15 @@ class Cache3_Test extends TestBase {
assertEquals("value", x.get("en", "US", 1, () -> "should not be
called"));
}
+ @Test
+ void b01b_put_withNullValue() {
+ var x = Cache3.of(String.class, String.class, Integer.class,
String.class).build();
+ x.put("en", "US", 1, "value1");
+ var previous = x.put("en", "US", 1, null);
+ assertEquals("value1", previous);
+ assertFalse(x.containsKey("en", "US", 1));
+ }
+
@Test
void b02_isEmpty() {
var x = Cache3.of(String.class, String.class, Integer.class,
String.class).build();
@@ -286,5 +295,50 @@ class Cache3_Test extends TestBase {
assertEquals(2, callCount.get());
assertTrue(x.isEmpty());
}
+
+
//====================================================================================================
+ // d - remove() and containsValue()
+
//====================================================================================================
+
+ @Test
+ void d01_remove() {
+ var x = Cache3.of(String.class, String.class, Integer.class,
String.class).build();
+ x.put("en", "US", 1, "value1");
+ var removed = x.remove("en", "US", 1);
+ assertEquals("value1", removed);
+ assertFalse(x.containsKey("en", "US", 1));
+ }
+
+ @Test
+ void d02_containsValue() {
+ var x = Cache3.of(String.class, String.class, Integer.class,
String.class).build();
+ x.put("en", "US", 1, "value1");
+ assertTrue(x.containsValue("value1"));
+ assertFalse(x.containsValue("value2"));
+ }
+
+
//====================================================================================================
+ // e - logOnExit() builder methods
+
//====================================================================================================
+
+ @Test
+ void e01_logOnExit_withStringId() {
+ var x = Cache3.of(String.class, String.class, Integer.class,
String.class)
+ .logOnExit("TestCache3")
+ .supplier((k1, k2, k3) -> k1 + ":" + k2 + ":" + k3)
+ .build();
+ x.get("en", "US", 1);
+ assertSize(1, x);
+ }
+
+ @Test
+ void e02_logOnExit_withBoolean() {
+ var x = Cache3.of(String.class, String.class, Integer.class,
String.class)
+ .logOnExit(true, "MyCache3")
+ .supplier((k1, k2, k3) -> k1 + ":" + k2 + ":" + k3)
+ .build();
+ x.get("en", "US", 1);
+ assertSize(1, x);
+ }
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache4_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache4_Test.java
index 2c687792e7..e2138cfaf9 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache4_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache4_Test.java
@@ -224,6 +224,15 @@ class Cache4_Test extends TestBase {
assertEquals("value", x.get("en", "US", "formal", 1, () ->
"should not be called"));
}
+ @Test
+ void b01b_put_withNullValue() {
+ var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class).build();
+ x.put("en", "US", "formal", 1, "value1");
+ var previous = x.put("en", "US", "formal", 1, null);
+ assertEquals("value1", previous);
+ assertFalse(x.containsKey("en", "US", "formal", 1));
+ }
+
@Test
void b02_isEmpty() {
var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class).build();
@@ -286,5 +295,50 @@ class Cache4_Test extends TestBase {
assertEquals(2, callCount.get());
assertTrue(x.isEmpty());
}
+
+
//====================================================================================================
+ // d - remove() and containsValue()
+
//====================================================================================================
+
+ @Test
+ void d01_remove() {
+ var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class).build();
+ x.put("en", "US", "formal", 1, "value1");
+ var removed = x.remove("en", "US", "formal", 1);
+ assertEquals("value1", removed);
+ assertFalse(x.containsKey("en", "US", "formal", 1));
+ }
+
+ @Test
+ void d02_containsValue() {
+ var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class).build();
+ x.put("en", "US", "formal", 1, "value1");
+ assertTrue(x.containsValue("value1"));
+ assertFalse(x.containsValue("value2"));
+ }
+
+
//====================================================================================================
+ // e - logOnExit() builder methods
+
//====================================================================================================
+
+ @Test
+ void e01_logOnExit_withStringId() {
+ var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class)
+ .logOnExit("TestCache4")
+ .supplier((k1, k2, k3, k4) -> k1 + ":" + k2 + ":" + k3
+ ":" + k4)
+ .build();
+ x.get("en", "US", "formal", 1);
+ assertSize(1, x);
+ }
+
+ @Test
+ void e02_logOnExit_withBoolean() {
+ var x = Cache4.of(String.class, String.class, String.class,
Integer.class, String.class)
+ .logOnExit(true, "MyCache4")
+ .supplier((k1, k2, k3, k4) -> k1 + ":" + k2 + ":" + k3
+ ":" + k4)
+ .build();
+ x.get("en", "US", "formal", 1);
+ assertSize(1, x);
+ }
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache5_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache5_Test.java
index 6f04a77f20..9a1c9871b9 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache5_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache5_Test.java
@@ -225,6 +225,15 @@ class Cache5_Test extends TestBase {
assertEquals("value", x.get("en", "US", "west", "formal", 1, ()
-> "should not be called"));
}
+ @Test
+ void b01b_put_withNullValue() {
+ var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class).build();
+ x.put("en", "US", "west", "formal", 1, "value1");
+ var previous = x.put("en", "US", "west", "formal", 1, null);
+ assertEquals("value1", previous);
+ assertFalse(x.containsKey("en", "US", "west", "formal", 1));
+ }
+
@Test
void b02_isEmpty() {
var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class).build();
@@ -287,5 +296,50 @@ class Cache5_Test extends TestBase {
assertEquals(2, callCount.get());
assertTrue(x.isEmpty());
}
+
+
//====================================================================================================
+ // d - remove() and containsValue()
+
//====================================================================================================
+
+ @Test
+ void d01_remove() {
+ var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class).build();
+ x.put("en", "US", "west", "formal", 1, "value1");
+ var removed = x.remove("en", "US", "west", "formal", 1);
+ assertEquals("value1", removed);
+ assertFalse(x.containsKey("en", "US", "west", "formal", 1));
+ }
+
+ @Test
+ void d02_containsValue() {
+ var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class).build();
+ x.put("en", "US", "west", "formal", 1, "value1");
+ assertTrue(x.containsValue("value1"));
+ assertFalse(x.containsValue("value2"));
+ }
+
+
//====================================================================================================
+ // e - logOnExit() builder methods
+
//====================================================================================================
+
+ @Test
+ void e01_logOnExit_withStringId() {
+ var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class)
+ .logOnExit("TestCache5")
+ .supplier((k1, k2, k3, k4, k5) -> k1 + ":" + k2 + ":" +
k3 + ":" + k4 + ":" + k5)
+ .build();
+ x.get("en", "US", "west", "formal", 1);
+ assertSize(1, x);
+ }
+
+ @Test
+ void e02_logOnExit_withBoolean() {
+ var x = Cache5.of(String.class, String.class, String.class,
String.class, Integer.class, String.class)
+ .logOnExit(true, "MyCache5")
+ .supplier((k1, k2, k3, k4, k5) -> k1 + ":" + k2 + ":" +
k3 + ":" + k4 + ":" + k5)
+ .build();
+ x.get("en", "US", "west", "formal", 1);
+ assertSize(1, x);
+ }
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache_Test.java
index 007742587d..e23e63eaf2 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/Cache_Test.java
@@ -620,6 +620,8 @@ class Cache_Test extends TestBase {
cache.put("key1", "value1");
var previous = cache.put("key1", null);
assertEquals("value1", previous);
+ // Null values are not cached, so key should be removed
+ assertFalse(cache.containsKey("key1"), "Key should be removed
when null value is put");
// Null values are not cached, so get() will call supplier
var callCount = new AtomicInteger();
var result = cache.get("key1", () -> {
@@ -628,6 +630,17 @@ class Cache_Test extends TestBase {
});
assertEquals("supplied", result);
assertEquals(1, callCount.get());
+ // After get() with non-null supplier, key is now in cache again
+ assertTrue(cache.containsKey("key1"), "Key should be in cache
after get() with non-null supplier");
+ }
+
+ @Test void a35b_put_withNullValue_newKey() {
+ var cache = Cache.of(String.class, String.class).build();
+ // Putting null for a new key should return null and not add
anything
+ var previous = cache.put("key1", null);
+ assertNull(previous);
+ assertFalse(cache.containsKey("key1"));
+ assertTrue(cache.isEmpty());
}
//====================================================================================================
@@ -705,6 +718,84 @@ class Cache_Test extends TestBase {
assertTrue(cache.containsKey(null));
}
+
//====================================================================================================
+ // remove() method
+
//====================================================================================================
+
+ @Test void a46_remove_existingKey() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.put("key1", "value1");
+ var removed = cache.remove("key1");
+ assertEquals("value1", removed);
+ assertFalse(cache.containsKey("key1"));
+ assertTrue(cache.isEmpty());
+ }
+
+ @Test void a47_remove_nonExistentKey() {
+ var cache = Cache.of(String.class, String.class).build();
+ var removed = cache.remove("key1");
+ assertNull(removed);
+ }
+
+ @Test void a48_remove_afterGet() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.get("key1", () -> "value1");
+ var removed = cache.remove("key1");
+ assertEquals("value1", removed);
+ assertFalse(cache.containsKey("key1"));
+ }
+
+ @Test void a49_remove_nullKey() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.put(null, "value1");
+ var removed = cache.remove(null);
+ assertEquals("value1", removed);
+ assertFalse(cache.containsKey(null));
+ }
+
+
//====================================================================================================
+ // containsValue() method
+
//====================================================================================================
+
+ @Test void a50_containsValue_present() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.put("key1", "value1");
+ cache.put("key2", "value2");
+ assertTrue(cache.containsValue("value1"));
+ assertTrue(cache.containsValue("value2"));
+ assertFalse(cache.containsValue("value3"));
+ }
+
+ @Test void a51_containsValue_notPresent() {
+ var cache = Cache.of(String.class, String.class).build();
+ assertFalse(cache.containsValue("value1"));
+ }
+
+ @Test void a52_containsValue_afterRemove() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.put("key1", "value1");
+ assertTrue(cache.containsValue("value1"));
+ cache.remove("key1");
+ assertFalse(cache.containsValue("value1"));
+ }
+
+ @Test void a53_containsValue_afterClear() {
+ var cache = Cache.of(String.class, String.class).build();
+ cache.put("key1", "value1");
+ cache.put("key2", "value2");
+ assertTrue(cache.containsValue("value1"));
+ cache.clear();
+ assertFalse(cache.containsValue("value1"));
+ assertFalse(cache.containsValue("value2"));
+ }
+
+ @Test void a54_containsValue_nullValue() {
+ var cache = Cache.of(String.class, String.class).build();
+ // Null values can't be cached, so containsValue(null) should
return false
+ cache.get("key1", () -> null);
+ assertFalse(cache.containsValue(null));
+ }
+
//====================================================================================================
// Array key support
//====================================================================================================
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/SimpleMap_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/SimpleMap_Test.java
index 6b8d7193af..148beb06f3 100755
---
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/SimpleMap_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/SimpleMap_Test.java
@@ -163,11 +163,66 @@ class SimpleMap_Test extends TestBase {
assertEquals("value4", map.get("key3"));
}
+
//====================================================================================================
+ // Array length mismatch
+
//====================================================================================================
+ @Test
+ void c01_arrayLengthMismatch_keysLonger() {
+ var keys = a("key1", "key2", "key3");
+ var values = a("value1", "value2");
+
+ IllegalArgumentException ex =
assertThrows(IllegalArgumentException.class, () -> {
+ new SimpleMap<>(keys, values);
+ });
+
+ assertTrue(ex.getMessage().contains("array lengths differ"));
+ assertTrue(ex.getMessage().contains("3")); // keys length
+ assertTrue(ex.getMessage().contains("2")); // values length
+ }
+
+ @Test
+ void c02_arrayLengthMismatch_valuesLonger() {
+ var keys = a("key1", "key2");
+ var values = a("value1", "value2", "value3", "value4");
+
+ IllegalArgumentException ex =
assertThrows(IllegalArgumentException.class, () -> {
+ new SimpleMap<>(keys, values);
+ });
+
+ assertTrue(ex.getMessage().contains("array lengths differ"));
+ assertTrue(ex.getMessage().contains("2")); // keys length
+ assertTrue(ex.getMessage().contains("4")); // values length
+ }
+
+ @Test
+ void c03_arrayLengthMismatch_emptyKeys() {
+ var keys = new String[0];
+ var values = a("value1");
+
+ IllegalArgumentException ex =
assertThrows(IllegalArgumentException.class, () -> {
+ new SimpleMap<>(keys, values);
+ });
+
+ assertTrue(ex.getMessage().contains("array lengths differ"));
+ }
+
+ @Test
+ void c04_arrayLengthMismatch_emptyValues() {
+ var keys = a("key1", "key2");
+ var values = new String[0];
+
+ IllegalArgumentException ex =
assertThrows(IllegalArgumentException.class, () -> {
+ new SimpleMap<>(keys, values);
+ });
+
+ assertTrue(ex.getMessage().contains("array lengths differ"));
+ }
+
//====================================================================================================
// Edge cases
//====================================================================================================
@Test
- void c01_emptyMap_noNullKeys() {
+ void c05_emptyMap_noNullKeys() {
String[] keys = {};
String[] values = {};
@@ -179,7 +234,7 @@ class SimpleMap_Test extends TestBase {
}
@Test
- void c02_getLookup_nullKeyNotInMap() {
+ void c06_getLookup_nullKeyNotInMap() {
String[] keys = { "key1", "key2" };
String[] values = { "value1", "value2" };
@@ -190,7 +245,7 @@ class SimpleMap_Test extends TestBase {
}
@Test
- void c03_putOperation_cannotAddNewNullKey() {
+ void c07_putOperation_cannotAddNewNullKey() {
String[] keys = { "key1" };
String[] values = { "value1" };
@@ -204,7 +259,7 @@ class SimpleMap_Test extends TestBase {
}
@Test
- void c04_complexTypes_nullKey() {
+ void c08_complexTypes_nullKey() {
var keys = a(1, null, 3);
var values = a("one", "null-key", "three");