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 2541dd7430 JUNEAU-248 BeanMap containsKey() and keySet().contains() 
don't match
2541dd7430 is described below

commit 2541dd74300878e02cd489a08d992673685faed8
Author: James Bognar <[email protected]>
AuthorDate: Mon Oct 13 15:56:47 2025 -0400

    JUNEAU-248 BeanMap containsKey() and keySet().contains() don't match
---
 .../apache/juneau/common/utils/StringUtils.java    | 10 ++++++--
 .../src/main/java/org/apache/juneau/BeanMap.java   | 13 ++++++++--
 .../java/org/apache/juneau/BeanMapErrors_Test.java | 29 +++++++++++-----------
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
index e47909c759..23ae2f7dbb 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringUtils.java
@@ -1575,10 +1575,16 @@ public class StringUtils {
                                                var key = s.substring(x+1, i);
                                                key = (hasInternalVar ? 
replaceVars(key, m) : key);
                                                hasInternalVar = false;
-                                               if (! m.containsKey(key))
+                                               // JUNEAU-248: Check if key 
exists in map by attempting to get it
+                                               // For regular maps: use 
containsKey() OR get() != null check
+                                               // For BeanMaps: get() returns 
non-null for accessible properties (including hidden ones)
+                                               var val = m.get(key);
+                                               // Check if key actually 
exists: either containsKey is true, or val is non-null
+                                               // This handles both regular 
maps and BeanMaps correctly
+                                               var keyExists = 
m.containsKey(key) || val != null;
+                                               if (! keyExists)
                                                        
out.append('{').append(key).append('}');
                                                else {
-                                                       var val = m.get(key);
                                                        if (val == null)
                                                                val = "";
                                                        var v = val.toString();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
index abc1e6fb3b..0a0886889e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
@@ -261,9 +261,18 @@ public class BeanMap<T> extends AbstractMap<String,Object> 
implements Delegate<T
 
        @Override /* Map */
        public boolean containsKey(Object property) {
-               if (getPropertyMeta(emptyIfNull(property)) != null)
+               // JUNEAU-248: Match the behavior of keySet() - only check 
properties map, not hiddenProperties
+               String key = emptyIfNull(property);
+               if (meta.properties.containsKey(key) && ! "*".equals(key))
                        return true;
-               return super.containsKey(property);
+               if (meta.dynaProperty != null) {
+                       try {
+                               return 
meta.dynaProperty.getDynaMap(bean).containsKey(key);
+                       } catch (Exception e) {
+                               throw new BeanRuntimeException(e);
+                       }
+               }
+               return false;
        }
 
        /**
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/BeanMapErrors_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/BeanMapErrors_Test.java
index ebbd2b61c5..08342ed08c 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/BeanMapErrors_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/BeanMapErrors_Test.java
@@ -30,15 +30,15 @@ class BeanMapErrors_Test extends TestBase {
 
        
//-----------------------------------------------------------------------------------------------------------------
        // @Beanp(name) on method not in @Bean(properties)
-       // Shouldn't be found in keySet()/entrySet() but should be found in 
containsKey()/get()
+       // JUNEAU-248: Shouldn't be found in keySet()/entrySet()/containsKey() 
but should be accessible via get()/put()
        
//-----------------------------------------------------------------------------------------------------------------
        @Test void beanPropertyMethodNotInBeanProperties() {
                var bc = BeanContext.DEFAULT;
 
                var bm = bc.newBeanMap(A1.class);
-               assertTrue(bm.containsKey("f2"));
-               assertEquals(-1, bm.get("f2"));
-               bm.put("f2", -2);
+               assertFalse(bm.containsKey("f2"));  // JUNEAU-248: Now 
consistent with keySet()
+               assertEquals(-1, bm.get("f2"));      // But get() still works
+               bm.put("f2", -2);                    // And put() still works
                assertEquals(-2, bm.get("f2"));
                assertFalse(bm.keySet().contains("f2"));
                
assertFalse(bm.entrySet().stream().map(Entry::getKey).toList().contains("f2"));
@@ -57,9 +57,9 @@ class BeanMapErrors_Test extends TestBase {
                var bc = 
BeanContext.create().applyAnnotations(B1Config.class).build();
 
                var bm = bc.newBeanMap(B1.class);
-               assertTrue(bm.containsKey("f2"));
-               assertEquals(-1, bm.get("f2"));
-               bm.put("f2", -2);
+               assertFalse(bm.containsKey("f2"));  // JUNEAU-248: Now 
consistent with keySet()
+               assertEquals(-1, bm.get("f2"));      // But get() still works
+               bm.put("f2", -2);                    // And put() still works
                assertEquals(-2, bm.get("f2"));
                assertFalse(bm.keySet().contains("f2"));
                
assertFalse(bm.entrySet().stream().map(Entry::getKey).toList().contains("f2"));
@@ -81,14 +81,15 @@ class BeanMapErrors_Test extends TestBase {
 
        
//-----------------------------------------------------------------------------------------------------------------
        // @Beanp(name) on field not in @Bean(properties)
+       // JUNEAU-248: Shouldn't be found in keySet()/entrySet()/containsKey() 
but should be accessible via get()/put()
        
//-----------------------------------------------------------------------------------------------------------------
        @Test void beanPropertyFieldNotInBeanProperties() {
                var bc = BeanContext.DEFAULT;
 
                var bm = bc.newBeanMap(A2.class);
-               assertTrue(bm.containsKey("f2"));
-               assertEquals(-1, bm.get("f2"));
-               bm.put("f2", -2);
+               assertFalse(bm.containsKey("f2"));  // JUNEAU-248: Now 
consistent with keySet()
+               assertEquals(-1, bm.get("f2"));      // But get() still works
+               bm.put("f2", -2);                    // And put() still works
                assertEquals(-2, bm.get("f2"));
                assertFalse(bm.keySet().contains("f2"));
                
assertFalse(bm.entrySet().stream().map(Entry::getKey).toList().contains("f2"));
@@ -102,13 +103,13 @@ class BeanMapErrors_Test extends TestBase {
                public int f2 = -1;
        }
 
-       @Test void beanPropertyFieldNotInBeanProperties_usingBeanConfig() {
+       @Test void beanPropertyFieldNotInBeanConfig() {
                var bc = 
BeanContext.create().applyAnnotations(B2Config.class).build();
 
                var bm = bc.newBeanMap(B2.class);
-               assertTrue(bm.containsKey("f2"));
-               assertEquals(-1, bm.get("f2"));
-               bm.put("f2", -2);
+               assertFalse(bm.containsKey("f2"));  // JUNEAU-248: Now 
consistent with keySet()
+               assertEquals(-1, bm.get("f2"));      // But get() still works
+               bm.put("f2", -2);                    // And put() still works
                assertEquals(-2, bm.get("f2"));
                assertFalse(bm.keySet().contains("f2"));
                
assertFalse(bm.entrySet().stream().map(Entry::getKey).toList().contains("f2"));

Reply via email to