Skip to content

Commit

Permalink
Merge pull request #5419 from eclipse-ee4j/mojarra_issue_5415
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
arjantijms committed Mar 17, 2024
2 parents 7cd3d2a + 51a688b commit c8cca6f
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 33 deletions.
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

0 comments on commit c8cca6f

Please sign in to comment.