Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#5415: move retargeted client ID calculation from AjaxHandler to AjaxBehaviorRenderer so it doesn't fail when cc.clientId happens to be referenced in f:ajax execute #5419

Merged
merged 1 commit into from Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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;
Expand All @@ -32,7 +33,7 @@

/**
* Basically represents {@code <f:ajax>} which is retargeted by {@code <cc:clientBehavior>} 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
Expand All @@ -41,9 +42,15 @@
public class RetargetedAjaxBehavior extends AjaxBehavior {

private AjaxBehavior retargeted;
private List<String> targetClientIds;

public RetargetedAjaxBehavior(AjaxBehavior retargeted) {
public RetargetedAjaxBehavior(AjaxBehavior retargeted, List<String> targetClientIds) {
this.retargeted = retargeted;
this.targetClientIds = targetClientIds;
}

public List<String> getTargetClientIds() {
return targetClientIds;
}

@Override
Expand Down
Expand Up @@ -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;
Expand All @@ -60,14 +51,21 @@
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;

/**
* <p class="changed_added_2_0">
* <span class="changed_modified_2_2">Enable</span> one or more components in the view to perform Ajax operations. This
* tag handler must create an instance of {@link jakarta.faces.component.behavior.AjaxBehavior} using the tag attribute
* values. <div class="changed_modified_2_2">The <code>events</code> attribute for this tag that can be a
* <code>ValueExpression</code> must be evaluated at tag execution time since the event name is used in the process of
* <code>Behavior</code> creation.</div> If this tag is nested within a single {@link ClientBehaviorHolder} component:
*
*
* <ul>
* <li>If the <code>events</code> attribute value is not specified, obtain the default event name by calling
* {@link ClientBehaviorHolder#getDefaultEventName}. If that returns <code>null</code> throw an
Expand Down Expand Up @@ -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<String> 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(" +")));
}
}
}
Expand Down
Expand Up @@ -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;
Expand All @@ -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;

/*
*<b>AjaxBehaviorRenderer</b> renders Ajax behavior for a component.
* It also
Expand Down Expand Up @@ -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<String> ids) {
private static void appendIds(FacesContext facesContext, UIComponent component, AjaxBehavior ajaxBehavior, StringBuilder builder, Collection<String> idsOrNull) {

if (idsOrNull == null) {
builder.append('0');
return;
}

Collection<String> 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<String> targetClientIds = ((RetargetedAjaxBehavior) ajaxBehavior).getTargetClientIds();
ids.remove("@this");
targetClientIds.stream().map(id -> "@this" + separatorChar + id).forEach(ids::add);
}

if (ids.isEmpty()) {
builder.append('0');
return;
}
Expand All @@ -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();

Expand Down