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

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new 2326a4e0 EMPIREDB-453 MyFaces: fix bug for value attribute expression 
inside composite components.
2326a4e0 is described below

commit 2326a4e04d1a12d57616760c9d9159c7e9fda006
Author: Rainer Döbele <[email protected]>
AuthorDate: Tue Jan 7 21:08:00 2025 +0100

    EMPIREDB-453
    MyFaces: fix bug for value attribute expression inside composite components.
---
 .../empire/jakarta/controls/InputControl.java      |  3 +-
 .../jakarta/controls/InputControlManager.java      | 12 +++++++
 .../empire/jakarta/impl/FacesImplementation.java   | 11 +++++++
 .../empire/jakarta/impl/MojarraImplementation.java | 25 ++++++++++-----
 .../empire/jakarta/impl/MyFacesImplementation.java | 37 +++++++++++++++++-----
 .../empire/jakarta/utils/TagEncodingHelper.java    | 15 +++++++++
 .../apache/empire/jsf2/controls/InputControl.java  |  3 +-
 .../empire/jsf2/controls/InputControlManager.java  | 12 +++++++
 .../empire/jsf2/impl/FacesImplementation.java      | 11 +++++++
 .../empire/jsf2/impl/MojarraImplementation.java    |  9 ++++++
 .../empire/jsf2/impl/MyFacesImplementation.java    | 21 ++++++++++++
 .../empire/jsf2/utils/TagEncodingHelper.java       | 15 +++++++++
 .../java/org/apache/empire/commons/ClassUtils.java | 20 ++++++++++++
 13 files changed, 176 insertions(+), 18 deletions(-)

diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
index 3234861d..4120697f 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
@@ -508,7 +508,8 @@ public abstract class InputControl
         /* -------------------------------------- */
 
         // Assign value
-        Object value = ii.getValue(false);
+        boolean evalExpression = 
!InputControlManager.isInputValueExpressionEnabled();
+        Object value = ii.getValue(evalExpression);
         if (value instanceof ValueExpression)
         {
             input.setValue(null);
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControlManager.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControlManager.java
index 2fdfb663..51064e53 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControlManager.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControlManager.java
@@ -106,6 +106,18 @@ public final class InputControlManager
         InputControlManager.attachedObjectsHandler = attachedObjectsHandler;
     }
 
+    static boolean inputValueExpressionEnabled = true;
+    
+    public static boolean isInputValueExpressionEnabled()
+    {
+        return inputValueExpressionEnabled;
+    }
+
+    public static void setInputValueExpressionEnabled(boolean 
valueExpressionEnabled)
+    {
+        inputValueExpressionEnabled = valueExpressionEnabled;
+    }
+
     private static Map<Class<? extends UIComponent>, String> componentTypeMap 
= new HashMap<Class<? extends UIComponent>, String>();
 
     @SuppressWarnings("unchecked")
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/FacesImplementation.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/FacesImplementation.java
index 107bcdf3..35216d3b 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/FacesImplementation.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/FacesImplementation.java
@@ -18,6 +18,8 @@
  */
 package org.apache.empire.jakarta.impl;
 
+import java.lang.reflect.Method;
+
 import jakarta.el.ELResolver;
 import jakarta.el.ValueExpression;
 import jakarta.faces.component.UIComponent;
@@ -106,6 +108,15 @@ public interface FacesImplementation
         */
        ValueExpression unwrapValueExpression(ValueExpression ve);
 
+    /**
+     * Returns a read or write method for an attribute
+     * @param component the component
+     * @param attribute the attribute name
+     * @param writeMethod flag wheter to the read or write method
+     * @return the method or null
+     */
+    Method getAttributeMethod(final UIComponent component, String attribute, 
boolean writeMethod);
+       
     /**
      * BeanStorageProvider
      * @author rainer
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MojarraImplementation.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MojarraImplementation.java
index 9b40b4db..0ae5b839 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MojarraImplementation.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MojarraImplementation.java
@@ -18,17 +18,10 @@
  */
 package org.apache.empire.jakarta.impl;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
-import jakarta.el.ELResolver;
-import jakarta.el.ValueExpression;
-import jakarta.faces.FacesException;
-import jakarta.faces.component.UIComponent;
-import jakarta.faces.context.ExternalContext;
-import jakarta.faces.context.FacesContext;
-import jakarta.servlet.ServletContext;
-
 import org.apache.empire.exceptions.ItemExistsException;
 import org.apache.empire.exceptions.NotSupportedException;
 import org.apache.empire.jakarta.utils.ValueExpressionUnwrapper;
@@ -47,6 +40,14 @@ import com.sun.faces.mgbean.ManagedBeanInfo;
 import com.sun.faces.spi.InjectionProvider;
 import com.sun.faces.spi.InjectionProviderException;
 
+import jakarta.el.ELResolver;
+import jakarta.el.ValueExpression;
+import jakarta.faces.FacesException;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.servlet.ServletContext;
+
 public class MojarraImplementation implements FacesImplementation 
 {
     // Logger
@@ -155,6 +156,14 @@ public class MojarraImplementation implements 
FacesImplementation
         return ValueExpressionUnwrapper.getInstance().unwrap(ve);
     }
     
+    @Override
+    public Method getAttributeMethod(final UIComponent component, String 
attribute, boolean writeMethod)
+    {
+        // Not yet implemented. 
+        // Is Implementation required?
+        return null;
+    }
+    
     private BeanStorageProvider beanStorage = null;
     
     @Override
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MyFacesImplementation.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MyFacesImplementation.java
index ed66c2f3..795d31bb 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MyFacesImplementation.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/impl/MyFacesImplementation.java
@@ -18,15 +18,9 @@
  */
 package org.apache.empire.jakarta.impl;
 
+import java.lang.reflect.Method;
 import java.util.List;
-
-import jakarta.el.ELContext;
-import jakarta.el.ELResolver;
-import jakarta.el.ValueExpression;
-import jakarta.faces.application.Application;
-import jakarta.faces.component.UIComponent;
-import jakarta.faces.context.ExternalContext;
-import jakarta.faces.context.FacesContext;
+import java.util.Map;
 
 import org.apache.empire.commons.ClassUtils;
 import org.apache.empire.exceptions.InternalException;
@@ -45,6 +39,14 @@ import 
org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import jakarta.el.ELContext;
+import jakarta.el.ELResolver;
+import jakarta.el.ValueExpression;
+import jakarta.faces.application.Application;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+
 public class MyFacesImplementation implements FacesImplementation 
 {
     // Logger
@@ -138,6 +140,25 @@ public class MyFacesImplementation implements 
FacesImplementation
         // now unwrap using the ValueExpressionUnwrapper 
         return ValueExpressionUnwrapper.getInstance().unwrap(ve);
     }
+    
+    @Override
+    public Method getAttributeMethod(final UIComponent component, String 
attribute, boolean writeMethod)
+    {
+        try {
+            // get the map
+            Map<String, Object> attrMap = component.getAttributes();
+            Object propDescHolder = 
ClassUtils.invokeMethod(attrMap.getClass(), attrMap, "getPropertyDescriptor", 
new Class<?>[] { String.class }, new Object[] { "value" }, true);
+            if (propDescHolder == null)
+                return null;
+            // Get the method
+            Method method = 
(Method)ClassUtils.invokeSimplePrivateMethod(propDescHolder, (writeMethod ? 
"getWriteMethod" : "getReadMethod"));
+            return method;
+        } catch(Exception e) {
+            // Not valid
+            log.warn("FacesImplementation.getAttributeMethod() for {} failed 
with exception: {}", attribute, e);
+            return null;
+        }
+    }
 
     private BeanStorageProvider beanStorage = null;
 
diff --git 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/utils/TagEncodingHelper.java
 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/utils/TagEncodingHelper.java
index 7c18f766..a1bfc6e1 100644
--- 
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/utils/TagEncodingHelper.java
+++ 
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/utils/TagEncodingHelper.java
@@ -20,6 +20,7 @@ package org.apache.empire.jakarta.utils;
 
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
@@ -30,6 +31,7 @@ import org.apache.commons.beanutils.BeanUtilsBean;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.commons.beanutils.PropertyUtilsBean;
 import org.apache.empire.commons.Attributes;
+import org.apache.empire.commons.ClassUtils;
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.Options;
 import org.apache.empire.commons.StringUtils;
@@ -68,6 +70,7 @@ import 
org.apache.empire.jakarta.controls.InputControl.ValueInfo;
 import org.apache.empire.jakarta.controls.InputControlManager;
 import org.apache.empire.jakarta.controls.SelectInputControl;
 import org.apache.empire.jakarta.controls.TextInputControl;
+import org.apache.empire.jakarta.impl.FacesImplementation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -826,7 +829,13 @@ public class TagEncodingHelper implements NamingContainer
                 if (value!=null && (component instanceof UIInput) && 
!((UIInput)component).isLocalValueSet())
                     value= null; /* should never come here! */
                 if (value==null)
+                {   // Check readMethod
+                    Method readMethod = 
FacesUtils.getFacesImplementation().getAttributeMethod(this.component, "value", 
false);
+                    if (readMethod!=null)
+                        return ClassUtils.invokeSimpleMethod(this.component, 
readMethod); // attribute is a javabean property!
+                    // use a value expression
                     value = findValueExpression("value");
+                }
                 // Return the local value or the ValueExpression if any
                 return value;
             }
@@ -936,6 +945,12 @@ public class TagEncodingHelper implements NamingContainer
         // if value expression, don't check further
         if (hasValueExpression()) 
         {   // check readonly
+            FacesImplementation impl = FacesUtils.getFacesImplementation();
+            if (impl.getAttributeMethod(this.component, "value", false)!=null)
+            {   // attribute is a javabean property! 
+                return (impl.getAttributeMethod(this.component, "value", 
true)==null);
+            }
+            // Check value expression
             ValueExpression ve = findValueExpression("value");
             return (ve!=null ? 
ve.isReadOnly(FacesContext.getCurrentInstance().getELContext()) : null);
         }
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
index 6ab5a116..68401e9c 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
@@ -508,7 +508,8 @@ public abstract class InputControl
         /* -------------------------------------- */
 
         // Assign value
-        Object value = ii.getValue(false);
+        boolean evalExpression = 
!InputControlManager.isInputValueExpressionEnabled();
+        Object value = ii.getValue(evalExpression);
         if (value instanceof ValueExpression)
         {
             input.setValue(null);
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
index 2fc55547..5a78ca48 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java
@@ -106,6 +106,18 @@ public final class InputControlManager
         InputControlManager.attachedObjectsHandler = attachedObjectsHandler;
     }
 
+    static boolean inputValueExpressionEnabled = true;
+    
+    public static boolean isInputValueExpressionEnabled()
+    {
+        return inputValueExpressionEnabled;
+    }
+
+    public static void setInputValueExpressionEnabled(boolean 
valueExpressionEnabled)
+    {
+        inputValueExpressionEnabled = valueExpressionEnabled;
+    }
+
     private static Map<Class<? extends UIComponent>, String> componentTypeMap 
= new HashMap<Class<? extends UIComponent>, String>();
 
     @SuppressWarnings("unchecked")
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/FacesImplementation.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/FacesImplementation.java
index 6c12fea9..a832965c 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/FacesImplementation.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/FacesImplementation.java
@@ -18,6 +18,8 @@
  */
 package org.apache.empire.jsf2.impl;
 
+import java.lang.reflect.Method;
+
 import javax.el.ELResolver;
 import javax.el.ValueExpression;
 import javax.faces.component.UIComponent;
@@ -106,6 +108,15 @@ public interface FacesImplementation
         */
        ValueExpression unwrapValueExpression(ValueExpression ve);
 
+    /**
+     * Returns a read or write method for an attribute
+     * @param component the component
+     * @param attribute the attribute name
+     * @param writeMethod flag wheter to the read or write method
+     * @return the method or null
+     */
+    Method getAttributeMethod(final UIComponent component, String attribute, 
boolean writeMethod);
+       
     /**
      * BeanStorageProvider
      * @author rainer
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MojarraImplementation.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MojarraImplementation.java
index d0148e86..dbc8fb7b 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MojarraImplementation.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MojarraImplementation.java
@@ -18,6 +18,7 @@
  */
 package org.apache.empire.jsf2.impl;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -155,6 +156,14 @@ public class MojarraImplementation implements 
FacesImplementation
         return ValueExpressionUnwrapper.getInstance().unwrap(ve);
     }
     
+    @Override
+    public Method getAttributeMethod(final UIComponent component, String 
attribute, boolean writeMethod)
+    {
+        // Not yet implemented. 
+        // Is Implementation required?
+        return null;
+    }
+    
     private BeanStorageProvider beanStorage = null;
     
     @Override
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MyFacesImplementation.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MyFacesImplementation.java
index eee0750d..e792cf21 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MyFacesImplementation.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/impl/MyFacesImplementation.java
@@ -18,7 +18,9 @@
  */
 package org.apache.empire.jsf2.impl;
 
+import java.lang.reflect.Method;
 import java.util.List;
+import java.util.Map;
 
 import javax.el.ELContext;
 import javax.el.ELResolver;
@@ -138,6 +140,25 @@ public class MyFacesImplementation implements 
FacesImplementation
         // now unwrap using the ValueExpressionUnwrapper 
         return ValueExpressionUnwrapper.getInstance().unwrap(ve);
     }
+    
+    @Override
+    public Method getAttributeMethod(final UIComponent component, String 
attribute, boolean writeMethod)
+    {
+        try {
+            // get the map
+            Map<String, Object> attrMap = component.getAttributes();
+            Object propDescHolder = 
ClassUtils.invokeMethod(attrMap.getClass(), attrMap, "getPropertyDescriptor", 
new Class<?>[] { String.class }, new Object[] { "value" }, true);
+            if (propDescHolder == null)
+                return null;
+            // Get the method
+            Method method = 
(Method)ClassUtils.invokeSimplePrivateMethod(propDescHolder, (writeMethod ? 
"getWriteMethod" : "getReadMethod"));
+            return method;
+        } catch(Exception e) {
+            // Not valid
+            log.warn("FacesImplementation.getAttributeMethod() for {} failed 
with exception: {}", attribute, e);
+            return null;
+        }
+    }
 
     private BeanStorageProvider beanStorage = null;
 
diff --git 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/TagEncodingHelper.java
 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/TagEncodingHelper.java
index 4e33739a..f6f311a5 100644
--- 
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/TagEncodingHelper.java
+++ 
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/TagEncodingHelper.java
@@ -20,6 +20,7 @@ package org.apache.empire.jsf2.utils;
 
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
@@ -44,6 +45,7 @@ import org.apache.commons.beanutils.BeanUtilsBean;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.commons.beanutils.PropertyUtilsBean;
 import org.apache.empire.commons.Attributes;
+import org.apache.empire.commons.ClassUtils;
 import org.apache.empire.commons.ObjectUtils;
 import org.apache.empire.commons.Options;
 import org.apache.empire.commons.StringUtils;
@@ -82,6 +84,7 @@ import org.apache.empire.jsf2.controls.InputControl.ValueInfo;
 import org.apache.empire.jsf2.controls.InputControlManager;
 import org.apache.empire.jsf2.controls.SelectInputControl;
 import org.apache.empire.jsf2.controls.TextInputControl;
+import org.apache.empire.jsf2.impl.FacesImplementation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -826,7 +829,13 @@ public class TagEncodingHelper implements NamingContainer
                 if (value!=null && (component instanceof UIInput) && 
!((UIInput)component).isLocalValueSet())
                     value= null; /* should never come here! */
                 if (value==null)
+                {   // Check readMethod
+                    Method readMethod = 
FacesUtils.getFacesImplementation().getAttributeMethod(this.component, "value", 
false);
+                    if (readMethod!=null)
+                        return ClassUtils.invokeSimpleMethod(this.component, 
readMethod); // attribute is a javabean property!
+                    // use a value expression
                     value = findValueExpression("value");
+                }
                 // Return the local value or the ValueExpression if any
                 return value;
             }
@@ -936,6 +945,12 @@ public class TagEncodingHelper implements NamingContainer
         // if value expression, don't check further
         if (hasValueExpression()) 
         {   // check readonly
+            FacesImplementation impl = FacesUtils.getFacesImplementation();
+            if (impl.getAttributeMethod(this.component, "value", false)!=null)
+            {   // attribute is a javabean property! 
+                return (impl.getAttributeMethod(this.component, "value", 
true)==null);
+            }
+            // Check value expression
             ValueExpression ve = findValueExpression("value");
             return (ve!=null ? 
ve.isReadOnly(FacesContext.getCurrentInstance().getELContext()) : null);
         }
diff --git a/empire-db/src/main/java/org/apache/empire/commons/ClassUtils.java 
b/empire-db/src/main/java/org/apache/empire/commons/ClassUtils.java
index 8887ba41..314d4eec 100644
--- a/empire-db/src/main/java/org/apache/empire/commons/ClassUtils.java
+++ b/empire-db/src/main/java/org/apache/empire/commons/ClassUtils.java
@@ -45,6 +45,8 @@ public final class ClassUtils
 {
     // Logger
     private static final Logger log = 
LoggerFactory.getLogger(ClassUtils.class);
+
+    public static final Object[] EMPTY_ARGS = new Object[0];
     
     /*
      * ClassUtils contains static methods only
@@ -670,6 +672,24 @@ public final class ClassUtils
         return invokeSimpleMethod(object.getClass(), object, methodName, true);
     }
 
+    /**
+     * Invoke a simple method (without parameters) on an object
+     * @param object the object on which to invoke the method
+     * @param method the method
+     * @return the return value
+     */
+    public static Object invokeSimpleMethod(Object object, Method method)
+    {
+        try
+        {
+            return method.invoke(object, EMPTY_ARGS);
+        }
+        catch (IllegalAccessException | IllegalArgumentException | 
InvocationTargetException e)
+        {
+            throw new NotSupportedException(object, method.getName(), e); 
+        } 
+    }
+    
     /**
      * Returns the JAR name that contains the implementation of a specific 
class
      * @param clazz the class 

Reply via email to