You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2022/12/21 12:27:38 UTC
[myfaces] branch 3.0.x updated: https://issues.apache.org/jira/browse/MYFACES-4040: Crossporting the fixes (#449)
This is an automated email from the ASF dual-hosted git repository.
werpu pushed a commit to branch 3.0.x
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/3.0.x by this push:
new 1d53bf4e7 https://issues.apache.org/jira/browse/MYFACES-4040: Crossporting the fixes (#449)
1d53bf4e7 is described below
commit 1d53bf4e729dbb1fbd891e776e5035115523a8b9
Author: Werner Punz <we...@apache.org>
AuthorDate: Wed Dec 21 13:27:32 2022 +0100
https://issues.apache.org/jira/browse/MYFACES-4040: Crossporting the fixes (#449)
from 2.3-next and main
---
.../search/MyFacesSearchExpressionHints.java | 35 ++
.../renderkit/html/HtmlAjaxBehaviorRenderer.java | 428 +--------------------
.../renderkit/html/HtmlCommandScriptRenderer.java | 364 ++----------------
.../renderkit/html/util/AjaxScriptBuilder.java | 370 ++++++++++++++++++
.../tag/jsf/html/DefaultHtmlDecoratorTestCase.java | 4 +-
.../apache/myfaces/shared/util/StringUtils.java | 36 ++
6 files changed, 484 insertions(+), 753 deletions(-)
diff --git a/impl/src/main/java/org/apache/myfaces/component/search/MyFacesSearchExpressionHints.java b/impl/src/main/java/org/apache/myfaces/component/search/MyFacesSearchExpressionHints.java
new file mode 100644
index 000000000..6a62ab5ac
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/component/search/MyFacesSearchExpressionHints.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.component.search;
+
+import java.util.EnumSet;
+import java.util.Set;
+import jakarta.faces.component.search.SearchExpressionHint;
+
+public class MyFacesSearchExpressionHints
+{
+ public static final Set<SearchExpressionHint> SET_IGNORE_NO_RESULT =
+ EnumSet.of(SearchExpressionHint.IGNORE_NO_RESULT);
+
+ public static final Set<SearchExpressionHint> SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT =
+ EnumSet.of(SearchExpressionHint.RESOLVE_CLIENT_SIDE, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
+
+ public static final Set<SearchExpressionHint> SET_RESOLVE_SINGLE_COMPONENT_IGNORE_NO_RESULT =
+ EnumSet.of(SearchExpressionHint.RESOLVE_SINGLE_COMPONENT, SearchExpressionHint.IGNORE_NO_RESULT);
+}
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
index 6419bfade..d67e26bf4 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
@@ -18,13 +18,6 @@
*/
package org.apache.myfaces.renderkit.html;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.RandomAccess;
-import java.util.Set;
import jakarta.faces.FacesException;
import jakarta.faces.component.ActionSource;
@@ -33,15 +26,15 @@ import jakarta.faces.component.UIComponent;
import jakarta.faces.component.behavior.AjaxBehavior;
import jakarta.faces.component.behavior.ClientBehavior;
import jakarta.faces.component.behavior.ClientBehaviorContext;
-import jakarta.faces.component.search.SearchExpressionContext;
-import jakarta.faces.component.search.SearchExpressionHandler;
-import jakarta.faces.component.search.SearchExpressionHint;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AjaxBehaviorEvent;
import jakarta.faces.event.PhaseId;
import jakarta.faces.render.ClientBehaviorRenderer;
+
+import org.apache.myfaces.renderkit.html.util.AjaxScriptBuilder;
import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
+
/**
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
@@ -49,30 +42,10 @@ import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
{
- private static final String QUOTE = "'";
- private static final String BLANK = " ";
-
- private static final String AJAX_KEY_ONERROR = "onerror";
- private static final String AJAX_KEY_ONEVENT = "onevent";
- private static final String AJAX_KEY_EXECUTE = "execute";
- private static final String AJAX_KEY_RENDER = "render";
- private static final String AJAX_KEY_DELAY = "delay";
- private static final String AJAX_KEY_RESETVALUES = "resetValues";
-
- private static final String AJAX_VAL_THIS = "this";
- private static final String AJAX_VAL_EVENT = "event";
- private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
-
- private static final String COLON = ":";
- private static final String EMPTY = "";
- private static final String COMMA = ",";
private static final String ERR_NO_AJAX_BEHAVIOR = "The behavior must be an instance of AjaxBehavior";
- private static final String L_PAREN = "(";
- private static final String R_PAREN = ")";
private static final String AJAX_SB = "oam.renderkit.AJAX_SB";
- private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
@Override
public void decode(FacesContext context, UIComponent component, ClientBehavior behavior)
@@ -98,7 +71,22 @@ public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
return null;
}
- return makeAjax(behaviorContext, ajaxBehavior).toString();
+ StringBuilder retVal = SharedStringBuilder.get(behaviorContext.getFacesContext(), AJAX_SB, 60);
+
+ AjaxScriptBuilder.build(behaviorContext.getFacesContext(),
+ retVal,
+ behaviorContext.getComponent(),
+ behaviorContext.getSourceId(),
+ behaviorContext.getEventName(),
+ ajaxBehavior.getExecute(),
+ ajaxBehavior.getRender(),
+ ajaxBehavior.getDelay(),
+ ajaxBehavior.isResetValues(),
+ ajaxBehavior.getOnerror(),
+ ajaxBehavior.getOnevent(),
+ behaviorContext.getParameters());
+
+ return retVal.toString();
}
private void dispatchBehaviorEvent(UIComponent component, AjaxBehavior ajaxBehavior)
@@ -137,384 +125,6 @@ public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
return isImmediate;
}
-
- /**
- * builds the generic ajax call depending upon
- * the ajax behavior parameters
- *
- * @param context the Client behavior context
- * @param behavior the behavior
- * @return a fully working javascript with calls into jsf.js
- */
- private StringBuilder makeAjax(ClientBehaviorContext context, AjaxBehavior behavior)
- {
- StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
- StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
-
- String executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, behavior.getExecute());
- String render = mapToString(context, paramBuffer, AJAX_KEY_RENDER, behavior.getRender());
-
- String onError = behavior.getOnerror();
- if (onError != null && !onError.trim().equals(EMPTY))
- {
- //onError = AJAX_KEY_ONERROR + COLON + onError;
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_ONERROR);
- paramBuffer.append(COLON);
- paramBuffer.append(onError);
- onError = paramBuffer.toString();
- }
- else
- {
- onError = null;
- }
- String onEvent = behavior.getOnevent();
- if (onEvent != null && !onEvent.trim().equals(EMPTY))
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_ONEVENT);
- paramBuffer.append(COLON);
- paramBuffer.append(onEvent);
- onEvent = paramBuffer.toString();
- }
- else
- {
- onEvent = null;
- }
- /*
- * since version 2.2
- */
- String delay = behavior.getDelay();
- if (delay != null && !delay.trim().equals(EMPTY))
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_DELAY);
- paramBuffer.append(COLON);
- if ("none".equals(delay))
- {
- paramBuffer.append('\'');
- paramBuffer.append(delay);
- paramBuffer.append('\'');
- }
- else
- {
- paramBuffer.append(delay);
- }
- delay = paramBuffer.toString();
- }
- else
- {
- delay = null;
- }
- /*
- * since version 2.2
- */
- String resetValues = Boolean.toString(behavior.isResetValues());
- if (resetValues.equals("true"))
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_RESETVALUES);
- paramBuffer.append(COLON);
- paramBuffer.append(resetValues);
- resetValues = paramBuffer.toString();
- }
- else
- {
- resetValues = null;
- }
-
- String sourceId = null;
- if (context.getSourceId() == null)
- {
- sourceId = AJAX_VAL_THIS;
- }
- else
- {
- paramBuffer.setLength(0);
- paramBuffer.append('\'');
- paramBuffer.append(context.getSourceId());
- paramBuffer.append('\'');
- sourceId = paramBuffer.toString();
-
- if (!context.getSourceId().trim().equals(
- context.getComponent().getClientId(context.getFacesContext())))
- {
- // Check if sourceId is not a clientId and there is no execute set
- UIComponent ref = context.getComponent();
- ref = (ref.getParent() == null) ? ref : ref.getParent();
- UIComponent instance = null;
- try
- {
- instance = ref.findComponent(context.getSourceId());
- }
- catch (IllegalArgumentException e)
- {
- // No Op
- }
- if (instance == null && executes == null)
- {
- // set the clientId of the component so the behavior can be decoded later,
- // otherwise the behavior will fail
- List<String> list = new ArrayList<String>();
- list.add(context.getComponent().getClientId(context.getFacesContext()));
- executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, list);
- }
- }
- }
-
-
- String event = context.getEventName();
-
- retVal.append(JS_AJAX_REQUEST);
- retVal.append(L_PAREN);
- retVal.append(sourceId);
- retVal.append(COMMA);
- retVal.append(AJAX_VAL_EVENT);
- retVal.append(COMMA);
-
- Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
- int paramSize = (params != null) ? params.size() : 0;
-
- List<String> parameterList = new ArrayList<>(paramSize + 2);
- if (executes != null)
- {
- parameterList.add(executes);
- }
- if (render != null)
- {
- parameterList.add(render);
- }
- if (onError != null)
- {
- parameterList.add(onError);
- }
- if (onEvent != null)
- {
- parameterList.add(onEvent);
- }
- /*
- * since version 2.2
- */
- if (delay != null)
- {
- parameterList.add(delay);
- }
- /*
- * since version 2.2
- */
- if (resetValues != null)
- {
- parameterList.add(resetValues);
- }
- if (paramSize > 0)
- {
- /**
- * see ClientBehaviorContext.html of the spec
- * the param list has to be added in the post back
- */
- // params are in 99% RamdonAccess instace created in
- // HtmlRendererUtils.getClientBehaviorContextParameters(Map<String, String>)
- if (params instanceof RandomAccess)
- {
- List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
- for (int i = 0, size = list.size(); i < size; i++)
- {
- ClientBehaviorContext.Parameter param = list.get(i);
- append(paramBuffer, parameterList, param);
- }
- }
- else
- {
- for (ClientBehaviorContext.Parameter param : params)
- {
- append(paramBuffer, parameterList, param);
- }
- }
- }
-
- //parameterList.add(QUOTE + BEHAVIOR_EVENT + QUOTE + COLON + QUOTE + event + QUOTE);
- paramBuffer.setLength(0);
- paramBuffer.append(QUOTE);
- paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
- paramBuffer.append(QUOTE);
- paramBuffer.append(COLON);
- paramBuffer.append(QUOTE);
- paramBuffer.append(event);
- paramBuffer.append(QUOTE);
- parameterList.add(paramBuffer.toString());
-
- /**
- * I assume here for now that the options are the same which also
- * can be sent via the options attribute to jakarta.faces.ajax
- * this still needs further clarifications but I assume so for now
- */
- retVal.append(buildOptions(paramBuffer, parameterList));
-
- retVal.append(R_PAREN);
-
- return retVal;
- }
-
- private void append(StringBuilder paramBuffer, List<String> parameterList, ClientBehaviorContext.Parameter param)
- {
- //TODO we may need a proper type handling in this part
- //lets leave it for now as it is
- //quotes etc.. should be transferred directly
- //and the rest is up to the toString properly implemented
- //ANS: Both name and value should be quoted
- paramBuffer.setLength(0);
- paramBuffer.append(QUOTE);
- paramBuffer.append(param.getName());
- paramBuffer.append(QUOTE);
- paramBuffer.append(COLON);
- paramBuffer.append(QUOTE);
- paramBuffer.append(param.getValue().toString());
- paramBuffer.append(QUOTE);
- parameterList.add(paramBuffer.toString());
- }
-
-
- private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
- {
- retVal.setLength(0);
-
- retVal.append("{");
-
- boolean first = true;
-
- for (int i = 0, size = options.size(); i < size; i++)
- {
- String option = options.get(i);
- if (option != null && !option.trim().equals(EMPTY))
- {
- if (!first)
- {
- retVal.append(COMMA);
- }
- else
- {
- first = false;
- }
- retVal.append(option);
- }
- }
- retVal.append("}");
- return retVal;
- }
-
- private String mapToString(ClientBehaviorContext context, StringBuilder retVal,
- String target, Collection<String> dataHolder)
- {
- //Clear buffer
- retVal.setLength(0);
-
- if (dataHolder == null)
- {
- dataHolder = Collections.emptyList();
- }
- int executeSize = dataHolder.size();
- if (executeSize > 0)
- {
-
- retVal.append(target);
- retVal.append(COLON);
- retVal.append(QUOTE);
-
- int cnt = 0;
-
- SearchExpressionContext searchExpressionContext = null;
-
- // perf: dataHolder is a Collection : ajaxBehaviour.getExecute()
- // and ajaxBehaviour.getRender() API
- // In most cases comes here a ArrayList, because
- // jakarta.faces.component.behavior.AjaxBehavior.getCollectionFromSpaceSplitString
- // creates it.
- if (dataHolder instanceof RandomAccess)
- {
- List<String> list = (List<String>) dataHolder;
- for (; cnt < executeSize; cnt++)
- {
- if (searchExpressionContext == null)
- {
- searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
- context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
- }
-
- String strVal = list.get(cnt);
- build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
- }
- }
- else
- {
- for (String strVal : dataHolder)
- {
- if (searchExpressionContext == null)
- {
- searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
- context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
- }
-
- cnt++;
- build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
- }
- }
-
- retVal.append(QUOTE);
- return retVal.toString();
- }
- return null;
-
- }
-
- private static final Set<SearchExpressionHint> EXPRESSION_HINTS =
- EnumSet.of(SearchExpressionHint.RESOLVE_CLIENT_SIDE, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
-
- public void build(ClientBehaviorContext context,
- int size, StringBuilder retVal, int cnt,
- String strVal, SearchExpressionContext searchExpressionContext)
- {
- strVal = strVal.trim();
- if (!EMPTY.equals(strVal))
- {
- /*
- if (!strVal.startsWith(IDENTIFYER_MARKER))
- {
- String componentId = getComponentId(context, strVal);
- retVal.append(componentId);
- }
- else
- {
- retVal.append(strVal);
- }*/
- SearchExpressionHandler handler = context.getFacesContext().getApplication().getSearchExpressionHandler();
- String clientId = handler.resolveClientId(searchExpressionContext, strVal);
- retVal.append(clientId);
- if (cnt < size)
- {
- retVal.append(BLANK);
- }
- }
- }
-
- /*
- private String getComponentId(ClientBehaviorContext context, String id)
- {
-
- UIComponent contextComponent = context.getComponent();
- UIComponent target = contextComponent.findComponent(id);
- if (target == null)
- {
- target = contextComponent.findComponent(
- context.getFacesContext().getNamingContainerSeparatorChar() + id);
- }
- if (target != null)
- {
- return target.getClientId(context.getFacesContext());
- }
- throw new FacesException("Component with id:" + id + " not found");
- }
- */
-
private void assertBehavior(ClientBehavior behavior)
{
if (!(behavior instanceof AjaxBehavior))
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
index eda8afcf6..f64b00d25 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
@@ -20,30 +20,23 @@
package org.apache.myfaces.renderkit.html;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
import java.util.List;
import java.util.Map;
-import java.util.RandomAccess;
-import java.util.Set;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIParameter;
-import jakarta.faces.component.behavior.AjaxBehavior;
import jakarta.faces.component.behavior.ClientBehaviorContext;
import jakarta.faces.component.behavior.ClientBehaviorHolder;
import jakarta.faces.component.html.HtmlCommandScript;
-import jakarta.faces.component.search.SearchExpressionContext;
-import jakarta.faces.component.search.SearchExpressionHandler;
-import jakarta.faces.component.search.SearchExpressionHint;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.event.ActionEvent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
+import org.apache.myfaces.renderkit.html.util.AjaxScriptBuilder;
import org.apache.myfaces.shared.renderkit.RendererUtils;
import org.apache.myfaces.shared.renderkit.html.HTML;
import org.apache.myfaces.shared.renderkit.html.HtmlRenderer;
import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
+import org.apache.myfaces.shared.renderkit.html.JavascriptContext;
import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
@@ -56,21 +49,7 @@ import org.apache.myfaces.shared.util.StringUtils;
type="jakarta.faces.Script")
public class HtmlCommandScriptRenderer extends HtmlRenderer
{
- private static final Set<SearchExpressionHint> EXPRESSION_HINTS =
- EnumSet.of(SearchExpressionHint.RESOLVE_CLIENT_SIDE, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
-
- private static final String AJAX_KEY_ONERROR = "onerror";
- private static final String AJAX_KEY_ONEVENT = "onevent";
- private static final String AJAX_KEY_EXECUTE = "execute";
- private static final String AJAX_KEY_RENDER = "render";
- private static final String AJAX_KEY_DELAY = "delay";
- private static final String AJAX_KEY_RESETVALUES = "resetValues";
-
- private static final String AJAX_VAL_THIS = "this";
- private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
-
private static final String AJAX_SB = "oam.renderkit.AJAX_SB";
- private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException
@@ -87,7 +66,7 @@ public class HtmlCommandScriptRenderer extends HtmlRenderer
writer.startElement(HTML.SCRIPT_ELEM, component);
writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
- HtmlRendererUtils.ScriptContext script = new HtmlRendererUtils.ScriptContext();
+ JavascriptContext script = new JavascriptContext();
//Write content
String cmdName = commandScript.getName();
@@ -106,23 +85,24 @@ public class HtmlCommandScriptRenderer extends HtmlRenderer
script.append("var "+name+" = function(o){var o=(typeof o==='object')&&o?o:{};");
script.prettyLine();
- // TODO ajaxBehavior not required actually....
- AjaxBehavior ajaxBehavior = new AjaxBehavior();
- Boolean resetValues = commandScript.getResetValues();
- if (resetValues != null)
- {
- ajaxBehavior.setResetValues(resetValues);
- }
- ajaxBehavior.setOnerror(commandScript.getOnerror());
- ajaxBehavior.setOnevent(commandScript.getOnevent());
-
- Collection<ClientBehaviorContext.Parameter> eventParameters = null;
- //eventParameters.add(new ClientBehaviorContext.Parameter("params", "o"));
- ClientBehaviorContext ccc = ClientBehaviorContext.createClientBehaviorContext(
- context, component, "action",
- commandScript.getClientId(context), eventParameters);
-
- script.append(makeAjax(context, ccc, ajaxBehavior, commandScript).toString());
+ List<UIParameter> uiParams = HtmlRendererUtils.getValidUIParameterChildren(
+ context, getChildren(commandScript), false, false);
+
+ StringBuilder ajax = SharedStringBuilder.get(context, AJAX_SB, 60);
+
+ AjaxScriptBuilder.build(context,
+ ajax,
+ commandScript,
+ commandScript.getClientId(context),
+ commandScript.getOnevent(),
+ commandScript.getExecute(),
+ commandScript.getRender(),
+ commandScript.getResetValues(),
+ commandScript.getOnerror(),
+ commandScript.getOnevent(),
+ uiParams);
+
+ script.append(ajax.toString());
script.decreaseIndent();
script.append("}");
@@ -183,7 +163,7 @@ public class HtmlCommandScriptRenderer extends HtmlRenderer
boolean activateActionEvent = false;
if (formInfo != null && !disabled)
{
- String reqValue = (String) facesContext.getExternalContext().getRequestParameterMap().get(
+ String reqValue = facesContext.getExternalContext().getRequestParameterMap().get(
HtmlRendererUtils.getHiddenCommandLinkFieldName(formInfo, facesContext));
activateActionEvent = reqValue != null && reqValue.equals(clientId)
|| HtmlRendererUtils.isPartialOrBehaviorSubmit(facesContext, clientId);
@@ -203,304 +183,4 @@ public class HtmlCommandScriptRenderer extends HtmlRenderer
HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
}
}
-
- /**
- * builds the generic ajax call depending upon
- * the ajax behavior parameters
- *
- * @param context the Client behavior context
- * @param behavior the behavior
- * @param commandScript the component
- * @return a fully working javascript with calls into jsf.js
- */
- private StringBuilder makeAjax(FacesContext facesContext, ClientBehaviorContext context, AjaxBehavior behavior,
- HtmlCommandScript commandScript)
- {
- StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
- StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
-
- SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
- context.getFacesContext(), context.getComponent(), EXPRESSION_HINTS, null);
-
- String executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE, commandScript.getExecute(),
- searchExpressionContext);
- String render = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_RENDER, commandScript.getRender(),
- searchExpressionContext);
-
- String onError = behavior.getOnerror();
- if (onError != null && !onError.trim().isEmpty())
- {
- //onError = AJAX_KEY_ONERROR + COLON + onError;
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_ONERROR);
- paramBuffer.append(':');
- paramBuffer.append(onError);
- onError = paramBuffer.toString();
- }
- else
- {
- onError = null;
- }
- String onEvent = behavior.getOnevent();
- if (onEvent != null && !onEvent.trim().isEmpty())
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_ONEVENT);
- paramBuffer.append(':');
- paramBuffer.append(onEvent);
- onEvent = paramBuffer.toString();
- }
- else
- {
- onEvent = null;
- }
-
- String delay = behavior.getDelay();
- if (delay != null && !delay.trim().isEmpty())
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_DELAY);
- paramBuffer.append(':');
- if ("none".equals(delay))
- {
- paramBuffer.append('\'');
- paramBuffer.append(delay);
- paramBuffer.append('\'');
- }
- else
- {
- paramBuffer.append(delay);
- }
- delay = paramBuffer.toString();
- }
- else
- {
- delay = null;
- }
-
- String resetValues = Boolean.toString(behavior.isResetValues());
- if (resetValues.equals("true"))
- {
- paramBuffer.setLength(0);
- paramBuffer.append(AJAX_KEY_RESETVALUES);
- paramBuffer.append(':');
- paramBuffer.append(resetValues);
- resetValues = paramBuffer.toString();
- }
- else
- {
- resetValues = null;
- }
-
- String sourceId = null;
- if (context.getSourceId() == null)
- {
- sourceId = AJAX_VAL_THIS;
- }
- else
- {
- paramBuffer.setLength(0);
- paramBuffer.append('\'');
- paramBuffer.append(context.getSourceId());
- paramBuffer.append('\'');
- sourceId = paramBuffer.toString();
-
- if (!context.getSourceId().trim().equals(
- context.getComponent().getClientId(context.getFacesContext())))
- {
- // Check if sourceId is not a clientId and there is no execute set
- UIComponent ref = context.getComponent();
- ref = (ref.getParent() == null) ? ref : ref.getParent();
- UIComponent instance = null;
- try
- {
- instance = ref.findComponent(context.getSourceId());
- }
- catch (IllegalArgumentException e)
- {
- // No Op
- }
- if (instance == null && executes == null)
- {
- // set the clientId of the component so the behavior can be decoded later,
- // otherwise the behavior will fail
- executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE,
- context.getComponent().getClientId(context.getFacesContext()), searchExpressionContext);
- }
- }
- }
-
- String event = context.getEventName();
-
- retVal.append(JS_AJAX_REQUEST);
- retVal.append('(');
- retVal.append(sourceId);
- retVal.append(",window.event,myfaces._impl._util._Lang.mixMaps(");
-
- Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
- int paramSize = (params != null) ? params.size() : 0;
-
- List<String> parameterList = new ArrayList<>(paramSize + 2);
- if (executes != null)
- {
- parameterList.add(executes);
- }
- if (render != null)
- {
- parameterList.add(render);
- }
- if (onError != null)
- {
- parameterList.add(onError);
- }
- if (onEvent != null)
- {
- parameterList.add(onEvent);
- }
- if (delay != null)
- {
- parameterList.add(delay);
- }
- if (resetValues != null)
- {
- parameterList.add(resetValues);
- }
- if (paramSize > 0)
- {
- /**
- * see ClientBehaviorContext.html of the spec
- * the param list has to be added in the post back
- */
- // params are in 99% RamdonAccess instace created in
- // HtmlRendererUtils.getClientBehaviorContextParameters(Map<String, String>)
- if (params instanceof RandomAccess)
- {
- List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
- for (int i = 0, size = list.size(); i < size; i++)
- {
- ClientBehaviorContext.Parameter param = list.get(i);
- append(paramBuffer, parameterList, param.getName(), param.getValue());
- }
- }
- else
- {
- for (ClientBehaviorContext.Parameter param : params)
- {
- append(paramBuffer, parameterList, param.getName(), param.getValue());
- }
- }
- }
-
- List<UIParameter> uiParams = HtmlRendererUtils.getValidUIParameterChildren(
- facesContext, commandScript.getChildren(), false, false);
- if (uiParams != null && uiParams.size() > 0)
- {
- for (int i = 0, size = uiParams.size(); i < size; i++)
- {
- UIParameter param = uiParams.get(i);
- append(paramBuffer, parameterList, param.getName(), param.getValue());
- }
- }
-
- paramBuffer.setLength(0);
- paramBuffer.append('\'');
- paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
- paramBuffer.append("\':\'");
- paramBuffer.append(event);
- paramBuffer.append('\'');
- parameterList.add(paramBuffer.toString());
-
- /**
- * I assume here for now that the options are the same which also
- * can be sent via the options attribute to jakarta.faces.ajax
- * this still needs further clarifications but I assume so for now
- */
- retVal.append(buildOptions(paramBuffer, parameterList));
-
- //mixMaps
- retVal.append(",o,false))");
-
- return retVal;
- }
-
- private void append(StringBuilder paramBuffer, List<String> parameterList, String paramName, Object paramValue)
- {
- //TODO we may need a proper type handling in this part
- //lets leave it for now as it is
- //quotes etc.. should be transferred directly
- //and the rest is up to the toString properly implemented
- //ANS: Both name and value should be quoted
- paramBuffer.setLength(0);
- paramBuffer.append('\'');
- paramBuffer.append(paramName);
- paramBuffer.append("\':\'");
- if (paramValue != null)
- {
- paramBuffer.append(paramValue.toString());
- }
- paramBuffer.append('\'');
- parameterList.add(paramBuffer.toString());
- }
-
- private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
- {
- retVal.setLength(0);
-
- retVal.append("{");
-
- boolean first = true;
-
- for (int i = 0, size = options.size(); i < size; i++)
- {
- String option = options.get(i);
- if (option != null && !option.trim().isEmpty())
- {
- if (!first)
- {
- retVal.append(',');
- }
- else
- {
- first = false;
- }
- retVal.append(option);
- }
- }
- retVal.append("}");
- return retVal;
- }
-
- private String resolveExpressionsAsParameter(StringBuilder retVal, String target, String expressions,
- SearchExpressionContext searchExpressionContext)
- {
- if (expressions != null && !expressions.trim().isEmpty())
- {
- retVal.setLength(0);
- retVal.append(target);
- retVal.append(':');
- retVal.append('\'');
-
- SearchExpressionHandler handler =
- searchExpressionContext.getFacesContext().getApplication().getSearchExpressionHandler();
- List<String> clientIds =
- handler.resolveClientIds(searchExpressionContext, expressions);
-
- if (clientIds != null && !clientIds.isEmpty())
- {
- for (int i = 0; i < clientIds.size(); i++)
- {
- if (i > 0)
- {
- retVal.append(' ');
- }
- retVal.append(clientIds.get(i));
- }
- }
-
- retVal.append('\'');
- return retVal.toString();
- }
-
- return null;
- }
}
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/util/AjaxScriptBuilder.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/util/AjaxScriptBuilder.java
new file mode 100644
index 000000000..a190cac0c
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/util/AjaxScriptBuilder.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.renderkit.html.util;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+import jakarta.faces.component.UIComponent;
+
+import jakarta.faces.component.UIParameter;
+import jakarta.faces.component.behavior.ClientBehaviorContext;
+
+import jakarta.faces.component.html.HtmlCommandScript;
+import jakarta.faces.component.search.SearchExpressionContext;
+import jakarta.faces.component.search.SearchExpressionHandler;
+import jakarta.faces.context.FacesContext;
+import org.apache.myfaces.component.search.MyFacesSearchExpressionHints;
+import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
+import org.apache.myfaces.shared.util.StringUtils;
+
+
+
+// CHECKSTYLE:OFF
+public class AjaxScriptBuilder
+{
+
+ private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
+
+ private static final String QUOTE = "'";
+ private static final String BLANK = " ";
+
+ private static final String AJAX_KEY_ONERROR = "onerror";
+ private static final String AJAX_KEY_ONEVENT = "onevent";
+ private static final String AJAX_KEY_EXECUTE = "execute";
+ private static final String AJAX_KEY_RENDER = "render";
+ private static final String AJAX_KEY_DELAY = "delay";
+ private static final String AJAX_KEY_RESETVALUES = "resetValues";
+
+ private static final String AJAX_VAL_THIS = "this";
+ private static final String AJAX_VAL_EVENT = "event";
+ private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
+
+ private static final String COLON = ":";
+ private static final String EMPTY = "";
+ private static final String COMMA = ",";
+
+ private static final String L_PAREN = "(";
+ private static final String R_PAREN = ")";
+
+ private static final String L_C_BRACE = "{";
+ private static final String R_C_BRACE = "}";
+ public static final String AJAX_KEY_PARAMS = "params";
+ public static final String AJAX_VAL_NULL = "null";
+
+
+ public static void build(FacesContext context,
+ StringBuilder sb,
+ UIComponent component,
+ String sourceId,
+ String eventName,
+ String execute,
+ String render,
+ Boolean resetValues,
+ String onerror,
+ String onevent,
+ List<UIParameter> uiParams)
+ {
+ build(context,
+ sb,
+ component,
+ sourceId,
+ eventName,
+ execute,
+ render,
+ null,
+ Boolean.TRUE.equals(resetValues) ? Boolean.TRUE.toString() : null,
+ onerror,
+ onevent,
+ null,
+ uiParams);
+ }
+
+ public static void build(FacesContext context,
+ StringBuilder sb,
+ UIComponent component,
+ String sourceId,
+ String eventName,
+ Collection<String> executeList,
+ Collection<String> renderList,
+ String delay,
+ boolean resetValues,
+ String onerror,
+ String onevent,
+ Collection<ClientBehaviorContext.Parameter> params)
+ {
+ String execute = null;
+ if (executeList != null && !executeList.isEmpty())
+ {
+ execute = String.join(BLANK, executeList);
+ }
+
+ String render = null;
+ if (renderList != null && !renderList.isEmpty())
+ {
+ render = String.join(BLANK, renderList);
+ }
+
+ build(context,
+ sb,
+ component,
+ sourceId,
+ eventName,
+ execute,
+ render,
+ delay,
+ resetValues ? Boolean.TRUE.toString() : null,
+ onerror,
+ onevent,
+ params,
+ null);
+ }
+
+ public static void build(FacesContext context,
+ StringBuilder sb,
+ UIComponent component,
+ String sourceId,
+ String eventName,
+ String execute,
+ String render,
+ String delay,
+ String resetValues,
+ String onerror,
+ String onevent,
+ Collection<ClientBehaviorContext.Parameter> params,
+ List<UIParameter> uiParams) {
+ // CHECKSTYLE:ON
+ HtmlCommandScript commandScript = (component instanceof HtmlCommandScript)
+ ? (HtmlCommandScript) component
+ : null;
+
+ sb.append(JS_AJAX_REQUEST + L_PAREN);
+
+ if (sourceId == null)
+ {
+ sb.append(AJAX_VAL_THIS);
+ }
+ else
+ {
+ sb.append(QUOTE);
+ sb.append(sourceId);
+ sb.append(QUOTE);
+
+ if (!sourceId.trim().equals(component.getClientId(context)))
+ {
+ // Check if sourceId is not a clientId and there is no execute set
+ UIComponent ref = (component.getParent() == null) ? component : component.getParent();
+ UIComponent instance = null;
+ try
+ {
+ instance = ref.findComponent(sourceId);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // No Op
+ }
+ if (instance == null && execute == null)
+ {
+ // set the clientId of the component so the behavior can be decoded later,
+ // otherwise the behavior will fail
+ execute = component.getClientId(context);
+ }
+ }
+ }
+ sb.append(COMMA);
+
+ sb.append(commandScript == null ? AJAX_VAL_EVENT : AJAX_VAL_NULL);
+
+
+ sb.append(COMMA);
+ sb.append(L_C_BRACE);
+
+ SearchExpressionHandler seHandler = null;
+ SearchExpressionContext seContext = null;
+ if (StringUtils.isNotBlank(execute) || StringUtils.isNotBlank(render))
+ {
+ seHandler = context.getApplication().getSearchExpressionHandler();
+ seContext = SearchExpressionContext.createSearchExpressionContext(
+ context, component,
+ MyFacesSearchExpressionHints.SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT, null);
+ }
+
+ if(!StringUtils.isBlank(execute))
+ {
+ appendIds(sb, AJAX_KEY_EXECUTE, execute, seHandler, seContext);
+ sb.append(COMMA);
+ }
+ if(!StringUtils.isBlank(render))
+ {
+ appendIds(sb, AJAX_KEY_RENDER, render, seHandler, seContext);
+ sb.append(COMMA);
+ }
+
+ if (onevent != null || onerror != null || delay != null || resetValues != null
+ || hasParams(eventName, params, uiParams))
+ {
+ if (onevent != null)
+ {
+ appendProperty(sb, AJAX_KEY_ONEVENT, onevent, false);
+ }
+ if (onerror != null)
+ {
+ appendProperty(sb, AJAX_KEY_ONERROR, onerror, false);
+ }
+ if (delay != null)
+ {
+ appendProperty(sb, AJAX_KEY_DELAY, delay, true);
+ }
+ if (resetValues != null)
+ {
+ appendProperty(sb, AJAX_KEY_RESETVALUES, resetValues, false);
+ }
+
+ if (!StringUtils.isBlank(eventName) || hasParams(eventName, params, uiParams))
+ {
+ StringBuilder paramsBuilder = SharedStringBuilder.get(context, AJAX_PARAM_SB, 60);
+ paramsBuilder.append(L_C_BRACE);
+ // In 4.0 we do this over our myfaces.ab call but given we are on a stable branch
+ // I do not want to mess with the ajax rendering
+ if(!StringUtils.isBlank(eventName))
+ {
+ appendProperty(paramsBuilder, ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME, eventName,
+ true);
+ }
+
+ if (params != null && !params.isEmpty())
+ {
+ if (params instanceof RandomAccess)
+ {
+ List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
+ for (int i = 0, size = list.size(); i < size; i++)
+ {
+ ClientBehaviorContext.Parameter param = list.get(i);
+ appendProperty(paramsBuilder, param.getName(), param.getValue(), true);
+ }
+ }
+ else
+ {
+ for (ClientBehaviorContext.Parameter param : params)
+ {
+ appendProperty(paramsBuilder, param.getName(), param.getValue(), true);
+ }
+ }
+ }
+
+ if (uiParams != null && uiParams.size() > 0)
+ {
+ for (int i = 0, size = uiParams.size(); i < size; i++)
+ {
+ UIParameter param = uiParams.get(i);
+ appendProperty(paramsBuilder, param.getName(), param.getValue(), true);
+ }
+ }
+
+
+
+ paramsBuilder.append(R_C_BRACE);
+ sb.append(AJAX_KEY_PARAMS);
+ sb.append(COLON);
+ sb.append(paramsBuilder);
+ }
+
+ }
+ sb.append('}');
+ sb.append(R_PAREN);
+ }
+
+ private static void appendIds(StringBuilder sb, String key, String expressions,
+ SearchExpressionHandler handler, SearchExpressionContext searchExpressionContext)
+ {
+ if(StringUtils.isBlank(expressions))
+ {
+ return;
+ }
+ sb.append(key + COLON);
+ sb.append(QUOTE);
+
+ if (StringUtils.isNotBlank(expressions))
+ {
+ List<String> clientIds =
+ handler.resolveClientIds(searchExpressionContext, expressions);
+
+ if (clientIds != null && !clientIds.isEmpty())
+ {
+ for (int i = 0; i < clientIds.size(); i++)
+ {
+ if (i > 0 && (i < clientIds.size() -1 ))
+ {
+ sb.append(' ');
+ }
+ sb.append(clientIds.get(i));
+ }
+ }
+ }
+
+ sb.append(QUOTE);
+ }
+
+
+ public static void appendProperty(StringBuilder builder,
+ String name,
+ Object value,
+ boolean quoteValue)
+ {
+ if (StringUtils.isBlank(name))
+ {
+ throw new IllegalArgumentException();
+ }
+
+ char lastChar = builder.charAt(builder.length() - 1);
+ if (!COMMA.equals(String.valueOf(lastChar)) && !L_C_BRACE.equals(String.valueOf(lastChar)))
+ {
+ builder.append(COMMA);
+ }
+
+ builder.append(QUOTE);
+ builder.append(name);
+ builder.append(QUOTE);
+
+ builder.append(COLON);
+
+ if (value == null)
+ {
+ builder.append(QUOTE+QUOTE);
+ }
+ else if (quoteValue)
+ {
+ builder.append(QUOTE);
+ builder.append(value);
+ builder.append(QUOTE);
+ }
+ else
+ {
+ builder.append(value);
+ }
+ }
+
+ private static boolean hasParams(String eventName, Collection<ClientBehaviorContext.Parameter> params,
+ List<UIParameter> uiParams)
+ {
+ return !StringUtils.isBlank(eventName) ||
+ (params != null && !params.isEmpty()) ||
+ (uiParams != null && !uiParams.isEmpty());
+ }
+}
diff --git a/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java
index aac8e67b9..b02433910 100644
--- a/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java
+++ b/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java
@@ -585,8 +585,8 @@ public class DefaultHtmlDecoratorTestCase extends FaceletTestCase
HtmlRenderedAttr[] attrs = new HtmlRenderedAttr[]{
new HtmlRenderedAttr("onclick",
"jsf.util.chain(this, event,'alert(\\'hello\\')', "
- + "'jsf.ajax.request(this,event,{render:\\'myForm:box5 \\',"
- + "\\'jakarta.faces.behavior.event\\':\\'click\\'})');"),
+ + "'jsf.ajax.request(this,event,{render:\\'myForm:box5\\',"
+ + "params:{\\'jakarta.faces.behavior.event\\':\\'click\\'}})');"),
};
HtmlCheckAttributesUtil.checkRenderedAttributes(attrs, sw.toString());
diff --git a/shared-public/src/main/java/org/apache/myfaces/shared/util/StringUtils.java b/shared-public/src/main/java/org/apache/myfaces/shared/util/StringUtils.java
index ba74d7579..17c04c242 100755
--- a/shared-public/src/main/java/org/apache/myfaces/shared/util/StringUtils.java
+++ b/shared-public/src/main/java/org/apache/myfaces/shared/util/StringUtils.java
@@ -37,6 +37,42 @@ public final class StringUtils
}
//~ Methods ------------------------------------------------------------------------------------
+ public static boolean isEmpty(String value)
+ {
+ return value == null || value.isEmpty();
+ }
+
+ public static boolean isBlank(String str)
+ {
+ if (str == null)
+ {
+ return true;
+ }
+ int strLen = str.length();
+ if (strLen == 0)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < strLen; i++)
+ {
+ if (!Character.isWhitespace(str.charAt(i)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isNotEmpty(String value)
+ {
+ return !isEmpty(value);
+ }
+
+ public static boolean isNotBlank(String value)
+ {
+ return !isBlank(value);
+ }
/**
* Checks that the string represents a floating point number that CANNOT be