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