This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new ad69d00c Suppress case variants of mapped names in 
isPropertySuppressed (#431)
ad69d00c is described below

commit ad69d00cec1637729c9d03950af25a27d9747565
Author: Naveed Khan <[email protected]>
AuthorDate: Thu Jul 30 11:03:47 2026 +0000

    Suppress case variants of mapped names in isPropertySuppressed (#431)
    
    * suppress case variants of mapped names in isPropertySuppressed
    
    The check compared the raw expression token, while the mapped-descriptor 
fallback it guards derives its accessor names from the capitalized property 
name, so a name differing only in the case of its first character resolved the 
same accessors and was still readable and writable.
    
    * skip null suppressed names in isPropertySuppressed
    
    SuppressPropertiesBeanIntrospector only rejects a null collection, so the
    suppressed set can hold null entries. Capitalizing them threw a
    NullPointerException where the previous Set.contains just never matched.
---
 .../beanutils2/MappedPropertyDescriptor.java       |  2 +-
 .../commons/beanutils2/PropertyUtilsBean.java      | 12 ++++++---
 .../commons/beanutils2/PropertyUtilsTest.java      | 31 ++++++++++++++++++++++
 3 files changed, 41 insertions(+), 4 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java 
b/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java
index 21d69d46..9a5b07e4 100644
--- a/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java
+++ b/src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java
@@ -162,7 +162,7 @@ public class MappedPropertyDescriptor extends 
PropertyDescriptor {
      *
      * @param s The property name
      */
-    private static String capitalizePropertyName(final String s) {
+    static String capitalizePropertyName(final String s) {
         if (s.isEmpty()) {
             return s;
         }
diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java 
b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
index be0f3066..36c653f0 100644
--- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
+++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
@@ -1068,15 +1068,21 @@ public class PropertyUtilsBean {
     /**
      * Tests whether a property name has been removed by a registered {@link 
SuppressPropertiesBeanIntrospector}. The mapped-descriptor fallback in
      * {@link #getPropertyDescriptor(Object, String)} bypasses the 
introspection pipeline, so suppressed mapped property names must be filtered 
explicitly.
+     * {@link MappedPropertyDescriptor} derives its accessor names from the 
capitalized property name, so names differing only in the case of their first
+     * character resolve the same accessors and are compared on that 
capitalized form here.
      *
      * @param name The property name to test.
      * @return {@code true} if the name is suppressed by an introspector.
      */
     private boolean isPropertySuppressed(final String name) {
+        final String base = 
MappedPropertyDescriptor.capitalizePropertyName(name);
         for (final BeanIntrospector introspector : introspectors) {
-            if (introspector instanceof SuppressPropertiesBeanIntrospector
-                    && ((SuppressPropertiesBeanIntrospector) 
introspector).getSuppressedProperties().contains(name)) {
-                return true;
+            if (introspector instanceof SuppressPropertiesBeanIntrospector) {
+                for (final String suppressed : 
((SuppressPropertiesBeanIntrospector) introspector).getSuppressedProperties()) {
+                    if (suppressed != null && 
base.equals(MappedPropertyDescriptor.capitalizePropertyName(suppressed))) {
+                        return true;
+                    }
+                }
             }
         }
         return false;
diff --git a/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java 
b/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
index 68338950..8e10a0ec 100644
--- a/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
+++ b/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
@@ -315,6 +315,37 @@ class PropertyUtilsTest {
         assertEquals("First Value", unsuppressed.getProperty(bean, 
"mappedProperty(First Key)"), "Mapped property should be readable when not 
suppressed");
     }
 
+    /**
+     * {@link MappedPropertyDescriptor} capitalizes the first character of the 
property name to build the accessor names, so {@code MappedProperty(key)}
+     * resolves the same {@code getMappedProperty}/{@code setMappedProperty} 
pair as {@code mappedProperty(key)} and must be suppressed with it.
+     */
+    @Test
+    void testCustomIntrospectionSuppressedMappedPropertyCaseVariant() throws 
Exception {
+        final PropertyUtilsBean pub = new PropertyUtilsBean();
+        pub.addBeanIntrospector(new 
SuppressPropertiesBeanIntrospector(Arrays.asList("mappedProperty")));
+
+        assertNull(pub.getPropertyDescriptor(bean, "MappedProperty"), "Case 
variant of a suppressed mapped property should have no descriptor");
+        assertThrows(NoSuchMethodException.class, () -> pub.getProperty(bean, 
"MappedProperty(First Key)"),
+                "Case variant of a suppressed mapped property must not be 
readable");
+        assertThrows(NoSuchMethodException.class, () -> pub.setProperty(bean, 
"MappedProperty(First Key)", "changed"),
+                "Case variant of a suppressed mapped property must not be 
writable");
+        assertEquals("First Value", bean.getMappedProperty("First Key"), 
"Suppressed mapped property must be unchanged");
+    }
+
+    /**
+     * {@link SuppressPropertiesBeanIntrospector} only rejects a null 
collection, so the set of suppressed names may hold null entries. Those must be 
skipped
+     * rather than capitalized when the mapped-descriptor fallback is checked.
+     */
+    @Test
+    void testCustomIntrospectionSuppressedMappedPropertyNullEntry() throws 
Exception {
+        final PropertyUtilsBean pub = new PropertyUtilsBean();
+        pub.addBeanIntrospector(new 
SuppressPropertiesBeanIntrospector(Arrays.asList(null, "mappedProperty")));
+
+        assertNull(pub.getPropertyDescriptor(bean, "MappedProperty"), "Case 
variant of a suppressed mapped property should have no descriptor");
+        assertNull(pub.getPropertyDescriptor(bean, "mappedProperty"), 
"Suppressed mapped property should have no descriptor");
+        assertNotNull(pub.getPropertyDescriptor(bean, "stringProperty"), "A 
null suppressed entry must not hide unrelated properties");
+    }
+
     /**
      * Test the describe() method.
      */

Reply via email to