You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2026/01/09 11:30:55 UTC

(myfaces) 01/02: Spec #2032 - Allow passthrough on facets

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

tandraschko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces.git

commit d3106b53b0729f8d534a9ea2a6e49c0696000d7f
Author: tandraschko <ta...@apache.org>
AuthorDate: Fri Jan 9 12:13:39 2026 +0100

    Spec #2032 - Allow passthrough on facets
---
 .../apache/myfaces/renderkit/RendererUtils.java    | 46 ++++++++++++++
 .../renderkit/html/base/HtmlGridRendererBase.java  |  3 +-
 .../renderkit/html/base/HtmlTableRendererBase.java | 28 ++++-----
 .../renderkit/html/util/HtmlRendererUtils.java     |  3 +-
 .../facelets/tag/faces/PassThroughLibrary.java     |  1 +
 .../view/facelets/tag/faces/core/FacetHandler.java | 70 +++++++++++++++++++++-
 6 files changed, 127 insertions(+), 24 deletions(-)

diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/RendererUtils.java b/impl/src/main/java/org/apache/myfaces/renderkit/RendererUtils.java
index 682d434b0..b6abf4db6 100755
--- a/impl/src/main/java/org/apache/myfaces/renderkit/RendererUtils.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/RendererUtils.java
@@ -45,6 +45,7 @@
 import jakarta.faces.component.UISelectOne;
 import jakarta.faces.component.ValueHolder;
 import jakarta.faces.context.FacesContext;
+import jakarta.faces.context.ResponseWriter;
 import jakarta.faces.convert.Converter;
 import jakarta.faces.convert.ConverterException;
 import jakarta.faces.model.SelectItem;
@@ -926,4 +927,49 @@ public String getAsString(FacesContext context, UIComponent component,
         }
 
     }
+
+    public static void renderFacet(FacesContext context, UIComponent facet) throws IOException
+    {
+        if (facet == null)
+        {
+            return;
+        }
+
+        renderPassthroughAttributes(context, facet);
+        //RendererUtils.renderChild(facesContext, facet);
+        facet.encodeAll(context);
+    }
+
+    public static void renderPassthroughAttributes(FacesContext context, UIComponent component) throws IOException
+    {
+        Map<String, Object> passthroughAttributes = component.getPassThroughAttributes(false);
+        if (passthroughAttributes != null && !passthroughAttributes.isEmpty())
+        {
+            ResponseWriter writer = context.getResponseWriter();
+            for (Map.Entry<String, Object> attribute : passthroughAttributes.entrySet())
+            {
+                Object attributeValue = attribute.getValue();
+                if (attributeValue != null)
+                {
+                    String value = null;
+                    if (attributeValue instanceof ValueExpression)
+                    {
+                        Object expressionValue = ((ValueExpression) attributeValue).getValue(context.getELContext());
+                        if (expressionValue != null)
+                        {
+                            value = expressionValue.toString();
+                        }
+                    }
+                    else
+                    {
+                        value = attributeValue.toString();
+                    }
+                    if (value != null)
+                    {
+                        writer.writeAttribute(attribute.getKey(), value, null);
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlGridRendererBase.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlGridRendererBase.java
index d71318de3..c64d058a0 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlGridRendererBase.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlGridRendererBase.java
@@ -234,8 +234,7 @@ protected void renderHeaderOrFooter(FacesContext context,
 
         writer.writeAttribute(HTML.COLSPAN_ATTR, Integer.toString(columns), null);
 
-        //RendererUtils.renderChild(context, facet);
-        facet.encodeAll(context);
+        RendererUtils.renderFacet(context, facet);
 
         writer.endElement(header ? HTML.TH_ELEM : HTML.TD_ELEM);
         writer.endElement(HTML.TR_ELEM);
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlTableRendererBase.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlTableRendererBase.java
index e3eaac0bb..b4ee2371e 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlTableRendererBase.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/HtmlTableRendererBase.java
@@ -238,9 +238,8 @@ protected void renderColgroupsFacet(FacesContext facesContext, ResponseWriter wr
             // no facet to be rendered
             return;
         }
-        // render the facet
-        //RendererUtils.renderChild(facesContext, colgroupsFacet);
-        colgroupsFacet.encodeAll(facesContext);
+
+        RendererUtils.renderFacet(facesContext, colgroupsFacet);
     } 
     
     /**
@@ -1161,11 +1160,9 @@ protected void renderTableHeaderOrFooterRow(FacesContext facesContext, ResponseW
         {
             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
         }
-        if (facet != null)
-        {
-            //RendererUtils.renderChild(facesContext, facet);
-            facet.encodeAll(facesContext);
-        }
+
+        RendererUtils.renderFacet(facesContext, facet);
+
         writer.endElement(colElementName);
         writer.endElement(HTML.TR_ELEM);
     }
@@ -1332,11 +1329,8 @@ protected void renderColumnHeaderCell(FacesContext facesContext, ResponseWriter
 
         writer.writeAttribute(HTML.SCOPE_ATTR, "col", null);
 
-        if (facet != null)
-        {
-            //RendererUtils.renderChild(facesContext, facet);
-            facet.encodeAll(facesContext);
-        }
+        RendererUtils.renderFacet(facesContext, facet);
+
         writer.endElement(determineHeaderCellTag(facesContext, uiComponent.getParent()));
     }
 
@@ -1384,11 +1378,9 @@ protected void renderColumnFooterCell(FacesContext facesContext, ResponseWriter
         {
             writer.writeAttribute(HTML.CLASS_ATTR, footerStyleClass, null);
         }
-        if (facet != null)
-        {
-            //RendererUtils.renderChild(facesContext, facet);
-            facet.encodeAll(facesContext);
-        }
+
+        RendererUtils.renderFacet(facesContext, facet);
+
         writer.endElement(HTML.TD_ELEM);
     }
 
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/util/HtmlRendererUtils.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/util/HtmlRendererUtils.java
index 3ab3be4c1..a5fc4dd54 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/util/HtmlRendererUtils.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/util/HtmlRendererUtils.java
@@ -882,8 +882,7 @@ else if (component instanceof HtmlDataTable dataTable)
         {
             writer.writeAttribute(HTML.STYLE_ATTR, captionStyle, null);
         }
-        //RendererUtils.renderChild(context, captionFacet);
-        captionFacet.encodeAll(context);
+        RendererUtils.renderFacet(context, captionFacet);
         writer.endElement(HTML.CAPTION_ELEM);
     }
 
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/PassThroughLibrary.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/PassThroughLibrary.java
index b535a18cf..b3d180bf2 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/PassThroughLibrary.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/PassThroughLibrary.java
@@ -32,6 +32,7 @@ public class PassThroughLibrary extends AbstractTagLibrary
     public final static String NAMESPACE = "jakarta.faces.passthrough";
     public final static String JCP_NAMESPACE = "http://xmlns.jcp.org/jsf/passthrough";
     public final static String SUN_NAMESPACE = "http://java.sun.com/jsf/passthrough";
+    public final static String[] NAMESPACES = new String[] {NAMESPACE, JCP_NAMESPACE, SUN_NAMESPACE};
 
     public final static PassThroughLibrary INSTANCE = new PassThroughLibrary();
     
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/core/FacetHandler.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/core/FacetHandler.java
index 4e71e5047..edec97dde 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/core/FacetHandler.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/faces/core/FacetHandler.java
@@ -19,18 +19,22 @@
 package org.apache.myfaces.view.facelets.tag.faces.core;
 
 import java.io.IOException;
+import java.util.Map;
 
 import jakarta.el.ELException;
 import jakarta.faces.FacesException;
 import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIPanel;
+
 import jakarta.faces.view.facelets.FaceletContext;
 import jakarta.faces.view.facelets.FaceletException;
+import jakarta.faces.view.facelets.Tag;
 import jakarta.faces.view.facelets.TagAttribute;
 import jakarta.faces.view.facelets.TagConfig;
 import jakarta.faces.view.facelets.TagException;
 import jakarta.faces.view.facelets.TagHandler;
-
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
+import org.apache.myfaces.view.facelets.tag.faces.PassThroughLibrary;
 
 /**
  * Register a named facet on the UIComponent associated with the closest parent UIComponent custom action. 
@@ -47,6 +51,7 @@ public final class FacetHandler extends TagHandler
 {
 
     public static final String KEY = "facelets.FACET_NAME";
+    public static final String FACET_HAS_PASSTHROUGH_ATTRIBUTES = "facelets.FACET_HAS_PASSTHROUGH_ATTRIBUTES";
 
     protected final TagAttribute name;
 
@@ -70,10 +75,30 @@ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, Fa
         {
             throw new TagException(this.tag, "Parent UIComponent was null");
         }
-        parent.getAttributes().put(KEY, this.name.getValue(ctx));
+
+        String facetName = this.name.getValue(ctx);
+        parent.getAttributes().put(KEY, facetName);
+
         try
         {
             this.nextHandler.apply(ctx, parent);
+
+            UIComponent child = parent.getFacets().get(facetName);
+            if (child == null && hasPassthroughAttributes(tag))
+            {
+                child = ctx.getFacesContext().getApplication().createComponent(UIPanel.COMPONENT_TYPE);
+                parent.getFacets().put(facetName, child);
+            }
+
+            if (child != null)
+            {
+                copyPassthroughAttributes(ctx, child, tag);
+                Map<String, Object> passThroughAttributes = child.getPassThroughAttributes(false);
+                if (passThroughAttributes != null && !passThroughAttributes.isEmpty())
+                {
+                    child.getTransientStateHelper().putTransient(FACET_HAS_PASSTHROUGH_ATTRIBUTES, true);
+                }
+            }
         }
         finally
         {
@@ -86,4 +111,45 @@ public String getFacetName(FaceletContext ctx)
     {
         return this.name.getValue(ctx);
     }
+
+    public static boolean hasFacetPassThroughAttributes(UIComponent possibleFacet)
+    {
+        return possibleFacet.getTransientStateHelper().getTransient(FACET_HAS_PASSTHROUGH_ATTRIBUTES) != null;
+    }
+
+    public static boolean hasPassthroughAttributes(Tag t)
+    {
+        for (String namespace : PassThroughLibrary.NAMESPACES)
+        {
+            TagAttribute[] passthroughAttrs = t.getAttributes().getAll(namespace);
+            if (passthroughAttrs != null && passthroughAttrs.length > 0)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static void copyPassthroughAttributes(FaceletContext ctx, UIComponent c, Tag t)
+    {
+        if (null == c || null == t)
+        {
+            return;
+        }
+
+        for (String namespace : PassThroughLibrary.NAMESPACES)
+        {
+            TagAttribute[] passthroughAttrs = t.getAttributes().getAll(namespace);
+            if (null != passthroughAttrs && 0 < passthroughAttrs.length)
+            {
+                Map<String, Object> componentPassthroughAttrs = c.getPassThroughAttributes(true);
+                Object attrValue = null;
+                for (TagAttribute cur : passthroughAttrs)
+                {
+                    attrValue = cur.isLiteral() ? cur.getValue(ctx) : cur.getValueExpression(ctx, Object.class);
+                    componentPassthroughAttrs.put(cur.getLocalName(), attrValue);
+                }
+            }
+        }
+    }
 }