From 51a688b0720f71cfffbbf823914d3d192b8eea8f Mon Sep 17 00:00:00 2001 From: Bauke Scholtz Date: Sat, 16 Mar 2024 11:38:24 -0400 Subject: [PATCH] Fix #5415: move retargeted client ID calculation from AjaxHandler to AjaxBehaviorHandler so it doesn't fail when cc.clientId happens to be referenced in f:ajax execute --- .../tag/composite/RetargetedAjaxBehavior.java | 11 ++++-- .../facelets/tag/faces/core/AjaxHandler.java | 34 +++++++------------ .../html_basic/AjaxBehaviorRenderer.java | 32 ++++++++++++----- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java b/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java index fd5a4b53db..eae9561328 100644 --- a/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java +++ b/impl/src/main/java/com/sun/faces/facelets/tag/composite/RetargetedAjaxBehavior.java @@ -17,6 +17,7 @@ package com.sun.faces.facelets.tag.composite; import java.util.Collection; +import java.util.List; import java.util.Set; import jakarta.el.ValueExpression; @@ -32,7 +33,7 @@ /** * Basically represents {@code } which is retargeted by {@code } in {@code AjaxHandler} and checked in {@code AjaxBehaviorRenderer}. - * + * * We should probably introduce {@code AjaxBehaviorWrapper} in Faces.next to reduce boilerplate like this. * * - https://github.com/jakartaee/faces/issues/1567 @@ -41,9 +42,15 @@ public class RetargetedAjaxBehavior extends AjaxBehavior { private AjaxBehavior retargeted; + private List targetClientIds; - public RetargetedAjaxBehavior(AjaxBehavior retargeted) { + public RetargetedAjaxBehavior(AjaxBehavior retargeted, List targetClientIds) { this.retargeted = retargeted; + this.targetClientIds = targetClientIds; + } + + public List getTargetClientIds() { + return targetClientIds; } @Override diff --git a/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java b/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java index dda42ad98d..a7d3422f37 100644 --- a/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java +++ b/impl/src/main/java/com/sun/faces/facelets/tag/faces/core/AjaxHandler.java @@ -16,26 +16,17 @@ package com.sun.faces.facelets.tag.faces.core; -import static jakarta.faces.component.UINamingContainer.getSeparatorChar; -import static java.util.Arrays.stream; +import static java.util.Arrays.asList; import java.beans.BeanDescriptor; import java.beans.BeanInfo; import java.io.IOException; import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.TreeSet; -import com.sun.faces.component.behavior.AjaxBehaviors; -import com.sun.faces.facelets.tag.TagHandlerImpl; -import com.sun.faces.facelets.tag.composite.BehaviorHolderWrapper; -import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior; -import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler; -import com.sun.faces.renderkit.RenderKitUtils; - import jakarta.el.ELContext; import jakarta.el.MethodExpression; import jakarta.el.MethodNotFoundException; @@ -60,6 +51,13 @@ import jakarta.faces.view.facelets.TagException; import jakarta.faces.view.facelets.TagHandler; +import com.sun.faces.component.behavior.AjaxBehaviors; +import com.sun.faces.facelets.tag.TagHandlerImpl; +import com.sun.faces.facelets.tag.composite.BehaviorHolderWrapper; +import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior; +import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler; +import com.sun.faces.renderkit.RenderKitUtils; + /** *

* Enable one or more components in the view to perform Ajax operations. This @@ -67,7 +65,7 @@ * values.

The events attribute for this tag that can be a * ValueExpression must be evaluated at tag execution time since the event name is used in the process of * Behavior creation.
If this tag is nested within a single {@link ClientBehaviorHolder} component: - * + * *
    *
  • If the events attribute value is not specified, obtain the default event name by calling * {@link ClientBehaviorHolder#getDefaultEventName}. If that returns null throw an @@ -302,20 +300,12 @@ private AjaxBehavior createAjaxBehavior(FaceletContext ctx, UIComponent parent, if (parent instanceof BehaviorHolderWrapper) { ValueExpression targets = ((BehaviorHolderWrapper) parent).getTargets(); - + if (targets != null) { String targetClientIds = (String) targets.getValue(ctx); - + if (targetClientIds != null) { - Collection executeClientIds = new ArrayList<>(behavior.getExecute()); - - if (executeClientIds.isEmpty() || executeClientIds.contains("@this")) { - String separatorChar = String.valueOf(getSeparatorChar(ctx.getFacesContext())); - executeClientIds.remove("@this"); - stream(targetClientIds.trim().split(" +")).map(id -> "@this" + separatorChar + id).forEach(executeClientIds::add); - behavior.setExecute(executeClientIds); - behavior = new RetargetedAjaxBehavior(behavior); - } + behavior = new RetargetedAjaxBehavior(behavior, asList(targetClientIds.trim().split(" +"))); } } } diff --git a/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java index 31842eff93..2c98537872 100644 --- a/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java +++ b/impl/src/main/java/com/sun/faces/renderkit/html_basic/AjaxBehaviorRenderer.java @@ -18,18 +18,16 @@ import static jakarta.faces.component.UINamingContainer.getSeparatorChar; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior; -import com.sun.faces.renderkit.RenderKitUtils; -import com.sun.faces.util.FacesLogger; - import jakarta.faces.component.ActionSource; import jakarta.faces.component.EditableValueHolder; import jakarta.faces.component.UIComponent; @@ -47,6 +45,10 @@ import jakarta.faces.event.PhaseId; import jakarta.faces.render.ClientBehaviorRenderer; +import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior; +import com.sun.faces.renderkit.RenderKitUtils; +import com.sun.faces.util.FacesLogger; + /* *AjaxBehaviorRenderer renders Ajax behavior for a component. * It also @@ -280,9 +282,24 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext, Aj SearchExpressionHint.RESOLVE_SINGLE_COMPONENT); // Appends an ids argument to the ajax command - private static void appendIds(FacesContext facesContext, UIComponent component, AjaxBehavior ajaxBehavior, StringBuilder builder, Collection ids) { + private static void appendIds(FacesContext facesContext, UIComponent component, AjaxBehavior ajaxBehavior, StringBuilder builder, Collection idsOrNull) { + + if (idsOrNull == null) { + builder.append('0'); + return; + } + + Collection ids = new ArrayList<>(idsOrNull); + UIComponent composite = UIComponent.getCompositeComponentParent(component); + String separatorChar = String.valueOf(getSeparatorChar(facesContext)); - if (null == ids || ids.isEmpty()) { + if (composite != null && (ajaxBehavior instanceof RetargetedAjaxBehavior) && (ids.isEmpty() || ids.contains("@this"))) { + List targetClientIds = ((RetargetedAjaxBehavior) ajaxBehavior).getTargetClientIds(); + ids.remove("@this"); + targetClientIds.stream().map(id -> "@this" + separatorChar + id).forEach(ids::add); + } + + if (ids.isEmpty()) { builder.append('0'); return; } @@ -294,9 +311,6 @@ private static void appendIds(FacesContext facesContext, UIComponent component, boolean first = true; - UIComponent composite = UIComponent.getCompositeComponentParent(component); - String separatorChar = String.valueOf(getSeparatorChar(facesContext)); - for (String id : ids) { String expression = id.trim();