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);
+ }
+ }
+ }
+ }
}