Skip to content

Commit

Permalink
https://github.com/jakartaee/faces/issues/1567
Browse files Browse the repository at this point in the history
Improved impl of "retargeted ajax behavior"
  • Loading branch information
BalusC committed Jul 30, 2023
1 parent e473437 commit 1f69399
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package com.sun.faces.facelets.tag.composite;

import java.util.Collection;
import java.util.Set;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.behavior.AjaxBehavior;
import javax.faces.component.behavior.ClientBehaviorContext;
import javax.faces.component.behavior.ClientBehaviorHint;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.AjaxBehaviorListener;
import javax.faces.event.BehaviorEvent;

/**
* Basically represents <f:ajax> which is retargeted by <cc:clientBehavior> in AjaxHandler and checked in AjaxBehaviorRenderer.
*
* @see https://github.com/jakartaee/faces/issues/1567
*/
public class RetargetedAjaxBehavior extends AjaxBehavior {

private AjaxBehavior retargeted;

public RetargetedAjaxBehavior(AjaxBehavior retargeted) {
this.retargeted = retargeted;
}

@Override
public String getScript(ClientBehaviorContext behaviorContext) {
return retargeted.getScript(behaviorContext);
}

@Override
public void broadcast(BehaviorEvent event) throws AbortProcessingException {
retargeted.broadcast(event);
}

@Override
public int hashCode() {
return retargeted.hashCode();
}

@Override
public String getRendererType() {
return retargeted.getRendererType();
}

@Override
public void decode(FacesContext context, UIComponent component) {
retargeted.decode(context, component);
}

@Override
public boolean isTransient() {
return retargeted.isTransient();
}

@Override
public Set<ClientBehaviorHint> getHints() {
return retargeted.getHints();
}

@Override
public void setTransient(boolean transientFlag) {
retargeted.setTransient(transientFlag);
}

@Override
public String getOnerror() {
return retargeted.getOnerror();
}

@Override
public void setOnerror(String onerror) {
retargeted.setOnerror(onerror);
}

@Override
public boolean equals(Object obj) {
return retargeted.equals(obj);
}

@Override
public String getOnevent() {
return retargeted.getOnevent();
}

@Override
public void setOnevent(String onevent) {
retargeted.setOnevent(onevent);
}

@Override
public void markInitialState() {
retargeted.markInitialState();
}

@Override
public boolean initialStateMarked() {
return retargeted.initialStateMarked();
}

@Override
public Collection<String> getExecute() {
return retargeted.getExecute();
}

@Override
public void clearInitialState() {
retargeted.clearInitialState();
}

@Override
public void setExecute(Collection<String> execute) {
retargeted.setExecute(execute);
}

@Override
public String getDelay() {
return retargeted.getDelay();
}

@Override
public void setDelay(String delay) {
retargeted.setDelay(delay);
}

@Override
public Collection<String> getRender() {
return retargeted.getRender();
}

@Override
public void setRender(Collection<String> render) {
retargeted.setRender(render);
}

@Override
public boolean isResetValues() {
return retargeted.isResetValues();
}

@Override
public void setResetValues(boolean resetValues) {
retargeted.setResetValues(resetValues);
}

@Override
public boolean isDisabled() {
return retargeted.isDisabled();
}

@Override
public void setDisabled(boolean disabled) {
retargeted.setDisabled(disabled);
}

@Override
public boolean isImmediate() {
return retargeted.isImmediate();
}

@Override
public void setImmediate(boolean immediate) {
retargeted.setImmediate(immediate);
}

@Override
public boolean isImmediateSet() {
return retargeted.isImmediateSet();
}

@Override
public String toString() {
return retargeted.toString();
}

@Override
public boolean isResetValuesSet() {
return retargeted.isResetValuesSet();
}

@Override
public ValueExpression getValueExpression(String name) {
return retargeted.getValueExpression(name);
}

@Override
public void setValueExpression(String name, ValueExpression binding) {
retargeted.setValueExpression(name, binding);
}

@Override
public void addAjaxBehaviorListener(AjaxBehaviorListener listener) {
retargeted.addAjaxBehaviorListener(listener);
}

@Override
public void removeAjaxBehaviorListener(AjaxBehaviorListener listener) {
retargeted.removeAjaxBehaviorListener(listener);
}

@Override
public Object saveState(FacesContext context) {
return retargeted.saveState(context);
}

@Override
public void restoreState(FacesContext context, Object state) {
retargeted.restoreState(context, state);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
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.jsf.CompositeComponentTagHandler;
import com.sun.faces.renderkit.RenderKitUtils;

Expand Down Expand Up @@ -345,6 +346,7 @@ private AjaxBehavior createAjaxBehavior(FaceletContext ctx, UIComponent parent,
executeClientIds.remove("@this");
stream(targetClientIds.trim().split(" +")).map(id -> "@this" + separatorChar + id).forEach(executeClientIds::add);
behavior.setExecute(executeClientIds);
behavior = new RetargetedAjaxBehavior(behavior);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import javax.faces.event.PhaseId;
import javax.faces.render.ClientBehaviorRenderer;

import com.sun.faces.facelets.tag.composite.InsertChildrenHandler;
import com.sun.faces.facelets.tag.composite.RetargetedAjaxBehavior;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.util.FacesLogger;

Expand Down Expand Up @@ -238,9 +238,9 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext,
ajaxCommand.append(eventName);
ajaxCommand.append("',");

appendIds(behaviorContext.getFacesContext(), component, ajaxCommand, execute);
appendIds(behaviorContext.getFacesContext(), component, ajaxBehavior, ajaxCommand, execute);
ajaxCommand.append(",");
appendIds(behaviorContext.getFacesContext(), component, ajaxCommand, render);
appendIds(behaviorContext.getFacesContext(), component, ajaxBehavior, ajaxCommand, render);

if ((onevent != null) || (onerror != null) || (delay != null) ||
(resetValues != null) || !params.isEmpty()) {
Expand Down Expand Up @@ -303,6 +303,7 @@ private static String buildAjaxCommand(ClientBehaviorContext behaviorContext,
// Appends an ids argument to the ajax command
private static void appendIds(FacesContext facesContext,
UIComponent component,
AjaxBehavior ajaxBehavior,
StringBuilder builder,
Collection<String> ids) {

Expand All @@ -318,7 +319,7 @@ private static void appendIds(FacesContext facesContext,

boolean first = true;

UIComponent composite = UIComponent.getCompositeComponentParent(component);
UIComponent composite = (ajaxBehavior instanceof RetargetedAjaxBehavior) ? UIComponent.getCompositeComponentParent(component) : null;
String separatorChar = String.valueOf(getSeparatorChar(facesContext));

for (String id : ids) {
Expand All @@ -335,7 +336,7 @@ private static void appendIds(FacesContext facesContext,

boolean clientResolveableExpression = expression.equals("@all") || expression.equals("@none") || expression.equals("@form") || expression.equals("@this");

if (composite != null && !isHandledByInsertChildren(component, composite) && (expression.equals("@this") || expression.startsWith("@this" + separatorChar))) {
if (composite != null && (expression.equals("@this") || expression.startsWith("@this" + separatorChar))) {
expression = expression.replaceFirst("@this", separatorChar + composite.getClientId(facesContext));
clientResolveableExpression = false;
}
Expand Down Expand Up @@ -372,16 +373,6 @@ private static void appendIds(FacesContext facesContext,
builder.append("'");
}

private static boolean isHandledByInsertChildren(UIComponent component, UIComponent composite) {
for (UIComponent parent = component.getParent(); parent != null && !parent.equals(composite); parent = parent.getParent()) {
if (parent.getAttributes().containsKey(InsertChildrenHandler.INDEX_ATTRIBUTE)) {
return true;
}
}

return false;
}

// Returns the resolved (client id) for a particular id.
private static String getResolvedId(UIComponent component, String id) {

Expand Down

0 comments on commit 1f69399

Please sign in to comment.